From f171095960d172d54015b28e8da302b5745dca86 Mon Sep 17 00:00:00 2001
From: Maxim Filippov <colixer@gmail.com>
Date: Wed, 6 Nov 2019 21:25:46 +1000
Subject: [PATCH] Grouped reports with status data baked in

---
 lib/pleroma/web/activity_pub/utils.ex         |  58 ++++-----
 .../web/admin_api/views/report_view.ex        |   4 +-
 .../admin_api/admin_api_controller_test.exs   | 121 ++++++++++++++----
 3 files changed, 123 insertions(+), 60 deletions(-)

diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 57349e304..5a51b7884 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
   alias Ecto.Changeset
   alias Ecto.UUID
   alias Pleroma.Activity
-  alias Pleroma.Activity.Queries
   alias Pleroma.Notification
   alias Pleroma.Object
   alias Pleroma.Repo
@@ -697,8 +696,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
           required(:groups) => [
             %{
               required(:date) => String.t(),
-              required(:account) => %User{},
-              required(:status) => %Activity{},
+              required(:account) => %{},
+              required(:status) => %{},
               required(:actors) => [%User{}],
               required(:reports) => [%Activity{}]
             }
@@ -706,32 +705,23 @@ defmodule Pleroma.Web.ActivityPub.Utils do
           required(:total) => integer
         }
   def get_reports_grouped_by_status do
-    paginated_activities = get_reported_status_ids()
-
     groups =
-      paginated_activities
+      get_reported_status_ids()
       |> Enum.map(fn entry ->
-        status =
-          Activity
-          |> Queries.by_ap_id(entry[:activity_id])
-          |> Activity.with_preloaded_object(:left)
-          |> Activity.with_preloaded_user_actor()
-          |> Repo.one()
-
-        reports = get_reports_by_status_id(status.data["id"])
-
-        max_date =
-          Enum.max_by(reports, &Pleroma.Web.CommonAPI.Utils.to_masto_date(&1.data["published"])).data[
-            "published"
-          ]
-
+        activity = Jason.decode!(entry.activity)
+        reports = get_reports_by_status_id(activity["id"])
+        max_date = Enum.max_by(reports, &NaiveDateTime.from_iso8601!(&1.data["published"]))
         actors = Enum.map(reports, & &1.user_actor)
 
         %{
-          date: max_date,
-          account: status.user_actor,
-          status: status,
-          actors: actors,
+          date: max_date.data["published"],
+          account: activity["actor"],
+          status: %{
+            id: activity["id"],
+            content: activity["content"],
+            published: activity["published"]
+          },
+          actors: Enum.uniq(actors),
           reports: reports
         }
       end)
@@ -741,28 +731,30 @@ defmodule Pleroma.Web.ActivityPub.Utils do
     }
   end
 
-  def get_reports_by_status_id(status_id) do
+  def get_reports_by_status_id(ap_id) do
     from(a in Activity,
       where: fragment("(?)->>'type' = 'Flag'", a.data),
-      where: fragment("(?)->'object' \\? (?)", a.data, ^status_id)
+      where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}])
     )
     |> Activity.with_preloaded_user_actor()
     |> Repo.all()
   end
 
-  @spec get_reported_status_ids() :: %{
-          required(:items) => [%Activity{}],
-          required(:total) => integer
-        }
+  @spec get_reported_status_ids() :: [
+          %{
+            required(:activity) => String.t(),
+            required(:date) => String.t()
+          }
+        ]
   def get_reported_status_ids do
     from(a in Activity,
       where: fragment("(?)->>'type' = 'Flag'", a.data),
       select: %{
         date: fragment("max(?->>'published') date", a.data),
-        activity_id:
-          fragment("jsonb_array_elements_text((? #- '{object,0}')->'object') activity_id", a.data)
+        activity:
+          fragment("jsonb_array_elements_text((? #- '{object,0}')->'object') activity", a.data)
       },
-      group_by: fragment("activity_id"),
+      group_by: fragment("activity"),
       order_by: fragment("date DESC")
     )
     |> Repo.all()
diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
index ac25925da..ca88595c7 100644
--- a/lib/pleroma/web/admin_api/views/report_view.ex
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -47,8 +47,8 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
       Enum.map(groups, fn group ->
         %{
           date: group[:date],
-          account: merge_account_views(group[:account]),
-          status: StatusView.render("show.json", %{activity: group[:status]}),
+          account: group[:account],
+          status: group[:status],
           actors: Enum.map(group[:actors], &merge_account_views/1),
           reports:
             group[:reports]
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 35367bed3..4e28c7774 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1551,7 +1551,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
   describe "GET /api/pleroma/admin/grouped_reports" do
     setup %{conn: conn} do
-      admin = insert(:user, info: %{is_admin: true})
+      admin = insert(:user, is_admin: true)
       [reporter, target_user] = insert_pair(:user)
 
       date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
@@ -1567,53 +1567,124 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       third_status =
         insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
 
-      %{
-        conn: assign(conn, :user, admin),
-        reporter: reporter,
-        target_user: target_user,
-        first_status: first_status,
-        second_status: second_status,
-        third_status: third_status
-      }
-    end
-
-    test "returns reports grouped by status", %{
-      conn: conn,
-      reporter: reporter,
-      target_user: target_user,
-      first_status: first_status,
-      second_status: second_status,
-      third_status: third_status
-    } do
-      {:ok, %{id: _}} =
+      {:ok, first_report} =
         CommonAPI.report(reporter, %{
           "account_id" => target_user.id,
           "status_ids" => [first_status.id, second_status.id, third_status.id]
         })
 
-      {:ok, %{id: _}} =
+      {:ok, second_report} =
         CommonAPI.report(reporter, %{
           "account_id" => target_user.id,
           "status_ids" => [first_status.id, second_status.id]
         })
 
-      {:ok, %{id: _}} =
+      {:ok, third_report} =
         CommonAPI.report(reporter, %{
           "account_id" => target_user.id,
           "status_ids" => [first_status.id]
         })
 
+      %{
+        conn: assign(conn, :user, admin),
+        first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
+        second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
+        third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
+        first_status_reports: [first_report, second_report, third_report],
+        second_status_reports: [first_report, second_report],
+        third_status_reports: [first_report],
+        target_user: target_user,
+        reporter: reporter
+      }
+    end
+
+    test "returns reports grouped by status", %{
+      conn: conn,
+      first_status: first_status,
+      second_status: second_status,
+      third_status: third_status,
+      first_status_reports: first_status_reports,
+      second_status_reports: second_status_reports,
+      third_status_reports: third_status_reports,
+      target_user: target_user,
+      reporter: reporter
+    } do
       response =
         conn
         |> get("/api/pleroma/admin/grouped_reports")
         |> json_response(:ok)
 
       assert length(response["reports"]) == 3
-      [third_group, second_group, first_group] = response["reports"]
 
-      assert length(third_group["reports"]) == 3
+      first_group =
+        Enum.find(response["reports"], &(&1["status"]["id"] == first_status.data["id"]))
+
+      second_group =
+        Enum.find(response["reports"], &(&1["status"]["id"] == second_status.data["id"]))
+
+      third_group =
+        Enum.find(response["reports"], &(&1["status"]["id"] == third_status.data["id"]))
+
+      assert length(first_group["reports"]) == 3
       assert length(second_group["reports"]) == 2
-      assert length(first_group["reports"]) == 1
+      assert length(third_group["reports"]) == 1
+
+      assert first_group["date"] ==
+               Enum.max_by(first_status_reports, fn act ->
+                 NaiveDateTime.from_iso8601!(act.data["published"])
+               end).data["published"]
+
+      assert first_group["status"] == %{
+               "id" => first_status.data["id"],
+               "content" => first_status.object.data["content"],
+               "published" => first_status.object.data["published"]
+             }
+
+      assert first_group["account"]["id"] == target_user.id
+
+      assert length(first_group["actors"]) == 1
+      assert hd(first_group["actors"])["id"] == reporter.id
+
+      assert Enum.map(first_group["reports"], & &1["id"]) --
+               Enum.map(first_status_reports, & &1.id) == []
+
+      assert second_group["date"] ==
+               Enum.max_by(second_status_reports, fn act ->
+                 NaiveDateTime.from_iso8601!(act.data["published"])
+               end).data["published"]
+
+      assert second_group["status"] == %{
+               "id" => second_status.data["id"],
+               "content" => second_status.object.data["content"],
+               "published" => second_status.object.data["published"]
+             }
+
+      assert second_group["account"]["id"] == target_user.id
+
+      assert length(second_group["actors"]) == 1
+      assert hd(second_group["actors"])["id"] == reporter.id
+
+      assert Enum.map(second_group["reports"], & &1["id"]) --
+               Enum.map(second_status_reports, & &1.id) == []
+
+      assert third_group["date"] ==
+               Enum.max_by(third_status_reports, fn act ->
+                 NaiveDateTime.from_iso8601!(act.data["published"])
+               end).data["published"]
+
+      assert third_group["status"] == %{
+               "id" => third_status.data["id"],
+               "content" => third_status.object.data["content"],
+               "published" => third_status.object.data["published"]
+             }
+
+      assert third_group["account"]["id"] == target_user.id
+
+      assert length(third_group["actors"]) == 1
+      assert hd(third_group["actors"])["id"] == reporter.id
+
+      assert Enum.map(third_group["reports"], & &1["id"]) --
+               Enum.map(third_status_reports, & &1.id) == []
     end
   end