Add User.list_inactive_users_query/1
This commit is contained in:
parent
aeafa0b2ef
commit
8add119444
|
@ -1447,4 +1447,42 @@ defp paginate(query, page, page_size) do
|
||||||
def showing_reblogs?(%User{} = user, %User{} = target) do
|
def showing_reblogs?(%User{} = user, %User{} = target) do
|
||||||
target.ap_id not in user.info.muted_reblogs
|
target.ap_id not in user.info.muted_reblogs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
The function returns a query to get users with no activity for given interval of days.
|
||||||
|
Inactive users are those who didn't read any notification, or had any activity where
|
||||||
|
the user is the activity's actor, during `inactivity_threshold` days.
|
||||||
|
Deactivated users will not appear in this list.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> Pleroma.User.list_inactive_users()
|
||||||
|
%Ecto.Query{}
|
||||||
|
"""
|
||||||
|
@spec list_inactive_users_query(integer()) :: Ecto.Query.t()
|
||||||
|
def list_inactive_users_query(inactivity_threshold \\ 7) do
|
||||||
|
negative_inactivity_threshold = -inactivity_threshold
|
||||||
|
now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
|
||||||
|
# Subqueries are not supported in `where` clauses, join gets too complicated.
|
||||||
|
has_read_notifications =
|
||||||
|
from(n in Pleroma.Notification,
|
||||||
|
where: n.seen == true,
|
||||||
|
group_by: n.id,
|
||||||
|
having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
|
||||||
|
select: n.user_id
|
||||||
|
)
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|
||||||
|
from(u in Pleroma.User,
|
||||||
|
left_join: a in Pleroma.Activity,
|
||||||
|
on: u.ap_id == a.actor,
|
||||||
|
where: not is_nil(u.nickname),
|
||||||
|
where: fragment("not (?->'deactivated' @> 'true')", u.info),
|
||||||
|
where: u.id not in ^has_read_notifications,
|
||||||
|
group_by: u.id,
|
||||||
|
having:
|
||||||
|
max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
|
||||||
|
is_nil(max(a.inserted_at))
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddSigninAndLastDigestDatesToUser do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:last_digest_emailed_at, :naive_datetime, default: fragment("now()"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1167,4 +1167,107 @@ test "follower count is updated when a follower is blocked" do
|
||||||
|
|
||||||
assert Map.get(user_show, "followers_count") == 2
|
assert Map.get(user_show, "followers_count") == 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "list_inactive_users_query/1" do
|
||||||
|
defp days_ago(days) do
|
||||||
|
NaiveDateTime.add(
|
||||||
|
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
|
||||||
|
-days * 60 * 60 * 24,
|
||||||
|
:second
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Users are inactive by default" do
|
||||||
|
total = 10
|
||||||
|
|
||||||
|
users =
|
||||||
|
Enum.map(1..total, fn _ ->
|
||||||
|
insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
|
||||||
|
end)
|
||||||
|
|
||||||
|
inactive_users_ids =
|
||||||
|
Pleroma.User.list_inactive_users_query()
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
Enum.each(users, fn user ->
|
||||||
|
assert user.id in inactive_users_ids
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Only includes users who has no recent activity" do
|
||||||
|
total = 10
|
||||||
|
|
||||||
|
users =
|
||||||
|
Enum.map(1..total, fn _ ->
|
||||||
|
insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
|
||||||
|
end)
|
||||||
|
|
||||||
|
{inactive, active} = Enum.split(users, trunc(total / 2))
|
||||||
|
|
||||||
|
Enum.map(active, fn user ->
|
||||||
|
to = Enum.random(users -- [user])
|
||||||
|
|
||||||
|
{:ok, _} =
|
||||||
|
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(user, %{
|
||||||
|
"status" => "hey @#{to.nickname}"
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
inactive_users_ids =
|
||||||
|
Pleroma.User.list_inactive_users_query()
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
Enum.each(active, fn user ->
|
||||||
|
refute user.id in inactive_users_ids
|
||||||
|
end)
|
||||||
|
|
||||||
|
Enum.each(inactive, fn user ->
|
||||||
|
assert user.id in inactive_users_ids
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Only includes users with no read notifications" do
|
||||||
|
total = 10
|
||||||
|
|
||||||
|
users =
|
||||||
|
Enum.map(1..total, fn _ ->
|
||||||
|
insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
|
||||||
|
end)
|
||||||
|
|
||||||
|
[sender | recipients] = users
|
||||||
|
{inactive, active} = Enum.split(recipients, trunc(total / 2))
|
||||||
|
|
||||||
|
Enum.each(recipients, fn to ->
|
||||||
|
{:ok, _} =
|
||||||
|
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{
|
||||||
|
"status" => "hey @#{to.nickname}"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, _} =
|
||||||
|
Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{
|
||||||
|
"status" => "hey again @#{to.nickname}"
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
Enum.each(active, fn user ->
|
||||||
|
[n1, _n2] = Pleroma.Notification.for_user(user)
|
||||||
|
{:ok, _} = Pleroma.Notification.read_one(user, n1.id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
inactive_users_ids =
|
||||||
|
Pleroma.User.list_inactive_users_query()
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
Enum.each(active, fn user ->
|
||||||
|
refute user.id in inactive_users_ids
|
||||||
|
end)
|
||||||
|
|
||||||
|
Enum.each(inactive, fn user ->
|
||||||
|
assert user.id in inactive_users_ids
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue