who_need_help/lib/who_need_help/accounts/data_lifecycle.ex

118 lines
3.7 KiB
Elixir

defmodule WhoNeedHelp.Accounts.DataLifecycle do
@moduledoc """
Read-only preflight for an account-deletion case.
This module intentionally does not erase or anonymise records. The applicable
retention policy is not encoded in the project, so an automatic destructive
action would make an unverified legal assumption. Operators get a repeatable,
audited checklist of live product state before a policy-backed executor is added.
"""
import Ecto.Query
alias WhoNeedHelp.Accounts
alias WhoNeedHelp.Accounts.{Scope, User}
alias WhoNeedHelp.Activities.{Activity, Participant}
alias WhoNeedHelp.Help.{Assignment, HelpRequest}
alias WhoNeedHelp.Repo
alias WhoNeedHelp.Support.SupportRequest
alias WhoNeedHelp.Tracking.TrackingSession
alias WhoNeedHelp.Trust.{AbuseSignal, Report}
def deletion_assessment(%Scope{user: moderator}, %SupportRequest{} = request) do
cond do
request.kind != :account_deletion ->
{:error, :not_deletion_request}
is_nil(request.requester_id) ->
{:error, :account_not_linked}
is_nil(request.contact_verified_at) ->
{:error, :contact_not_verified}
true ->
user = Repo.get(User, request.requester_id)
if user do
counts = blocking_counts(user.id)
blockers =
counts
|> Enum.filter(fn {_name, count} -> count > 0 end)
|> Enum.map(fn {name, count} -> %{kind: name, count: count} end)
{:ok,
%{
case_id: request.id,
reference: request.reference,
account_id: user.id,
account_staff_roles: Accounts.staff_roles(user),
contact_verified: true,
assessed_by: moderator.id,
assessed_at: DateTime.utc_now(:second),
blocking_counts: counts,
blockers: blockers,
technically_idle: blockers == [],
execution_available: false,
execution_blocker: :retention_policy_not_configured
}}
else
{:error, :account_not_found}
end
end
end
defp blocking_counts(user_id) do
%{
active_help_requests:
HelpRequest
|> where(
[request],
request.requester_id == ^user_id and
request.status in [:open, :matched, :in_progress]
)
|> count(),
active_helper_assignments:
Assignment
|> where(
[assignment],
assignment.helper_id == ^user_id and assignment.active and
assignment.status in [:accepted, :in_progress]
)
|> count(),
active_tracking_sessions:
TrackingSession
|> where([session], session.user_id == ^user_id and session.active)
|> count(),
open_activities:
Activity
|> where([activity], activity.creator_id == ^user_id and activity.status == :open)
|> count(),
active_activity_memberships:
Participant
|> join(:inner, [participant], activity in Activity,
on: activity.id == participant.activity_id
)
|> where(
[participant, activity],
participant.user_id == ^user_id and activity.status == :open and
participant.status in [:requested, :approved]
)
|> Repo.aggregate(:count),
unresolved_reports:
Report
|> where(
[report],
report.reporter_id == ^user_id and report.status in [:open, :reviewing]
)
|> count(),
open_trust_signals:
AbuseSignal
|> where([signal], signal.subject_id == ^user_id and signal.status == :open)
|> count()
}
end
defp count(query), do: Repo.aggregate(query, :count)
end