diff --git a/lib/wanderer_app/map.ex b/lib/wanderer_app/map.ex index 9fa6c50b..03c8b952 100644 --- a/lib/wanderer_app/map.ex +++ b/lib/wanderer_app/map.ex @@ -527,20 +527,22 @@ defmodule WandererApp.Map do @doc """ Returns the raw activity data that can be processed by WandererApp.Character.Activity. Only includes characters that are on the map's ACL. + If days parameter is provided, filters activity to that time period. """ - def get_character_activity(map_id) do + def get_character_activity(map_id, days \\ nil) do {:ok, map} = WandererApp.Api.Map.by_id(map_id) _map_with_acls = Ash.load!(map, :acls) - {:ok, jumps} = WandererApp.Api.MapChainPassages.by_map_id(%{map_id: map_id}) - thirty_days_ago = DateTime.utc_now() |> DateTime.add(-30 * 24 * 3600, :second) + # Calculate cutoff date if days is provided + cutoff_date = if days, do: DateTime.utc_now() |> DateTime.add(-days * 24 * 3600, :second), else: nil # Get activity data - connections_activity = get_connections_activity(map_id, thirty_days_ago) - signatures_activity = get_signatures_activity(map_id, thirty_days_ago) + passages_activity = get_passages_activity(map_id, cutoff_date) + connections_activity = get_connections_activity(map_id, cutoff_date) + signatures_activity = get_signatures_activity(map_id, cutoff_date) - # Return raw activity data - jumps + # Return activity data + passages_activity |> Enum.map(fn passage -> %{ character: passage.character, @@ -554,14 +556,40 @@ defmodule WandererApp.Map do end) end - defp get_connections_activity(map_id, thirty_days_ago) do + defp get_passages_activity(map_id, nil) do + # Query all map chain passages without time filter + from(p in WandererApp.Api.MapChainPassages, + join: c in assoc(p, :character), + where: p.map_id == ^map_id, + group_by: [c.id], + select: {c, count(p.id)} + ) + |> WandererApp.Repo.all() + |> Enum.map(fn {character, count} -> %{character: character, count: count} end) + end + + defp get_passages_activity(map_id, cutoff_date) do + # Query map chain passages with time filter + from(p in WandererApp.Api.MapChainPassages, + join: c in assoc(p, :character), + where: + p.map_id == ^map_id and + p.inserted_at > ^cutoff_date, + group_by: [c.id], + select: {c, count(p.id)} + ) + |> WandererApp.Repo.all() + |> Enum.map(fn {character, count} -> %{character: character, count: count} end) + end + + defp get_connections_activity(map_id, nil) do + # Query all connection activity without time filter from(ua in WandererApp.Api.UserActivity, join: c in assoc(ua, :character), where: ua.entity_id == ^map_id and ua.entity_type == :map and - ua.event_type == :map_connection_added and - ua.inserted_at > ^thirty_days_ago, + ua.event_type == :map_connection_added, group_by: [c.id], select: {c.id, count(ua.id)} ) @@ -569,14 +597,43 @@ defmodule WandererApp.Map do |> Map.new() end - defp get_signatures_activity(map_id, thirty_days_ago) do + defp get_connections_activity(map_id, cutoff_date) do + from(ua in WandererApp.Api.UserActivity, + join: c in assoc(ua, :character), + where: + ua.entity_id == ^map_id and + ua.entity_type == :map and + ua.event_type == :map_connection_added and + ua.inserted_at > ^cutoff_date, + group_by: [c.id], + select: {c.id, count(ua.id)} + ) + |> WandererApp.Repo.all() + |> Map.new() + end + + defp get_signatures_activity(map_id, nil) do + # Query all signature activity without time filter + from(ua in WandererApp.Api.UserActivity, + join: c in assoc(ua, :character), + where: + ua.entity_id == ^map_id and + ua.entity_type == :map and + ua.event_type == :signatures_added, + select: {ua.character_id, ua.event_data} + ) + |> WandererApp.Repo.all() + |> process_signatures_data() + end + + defp get_signatures_activity(map_id, cutoff_date) do from(ua in WandererApp.Api.UserActivity, join: c in assoc(ua, :character), where: ua.entity_id == ^map_id and ua.entity_type == :map and ua.event_type == :signatures_added and - ua.inserted_at > ^thirty_days_ago, + ua.inserted_at > ^cutoff_date, select: {ua.character_id, ua.event_data} ) |> WandererApp.Repo.all() diff --git a/lib/wanderer_app_web/controllers/map_api_controller.ex b/lib/wanderer_app_web/controllers/map_api_controller.ex index 11966cf2..328f09b4 100644 --- a/lib/wanderer_app_web/controllers/map_api_controller.ex +++ b/lib/wanderer_app_web/controllers/map_api_controller.ex @@ -648,15 +648,17 @@ defmodule WandererAppWeb.MapAPIController do Returns character activity data for a map. Requires either `?map_id=` or `?slug=`. + Optional `days` parameter to filter activity to a specific time period. Example: GET /api/map/character_activity?map_id= GET /api/map/character_activity?slug= + GET /api/map/character_activity?map_id=&days=7 """ @spec character_activity(Plug.Conn.t(), map()) :: Plug.Conn.t() operation :character_activity, summary: "Get Character Activity", - description: "Returns character activity data for a map. Requires either 'map_id' or 'slug' as a query parameter to identify the map.", + description: "Returns character activity data for a map. If days parameter is provided, filters activity to that time period, otherwise returns all activity. Requires either 'map_id' or 'slug' as a query parameter to identify the map.", parameters: [ map_id: [ in: :query, @@ -671,6 +673,13 @@ defmodule WandererAppWeb.MapAPIController do type: :string, required: false, example: "map-name" + ], + days: [ + in: :query, + description: "Optional: Number of days to look back for activity data. If not provided, returns all activity history.", + type: :integer, + required: false, + example: "7" ] ], responses: [ @@ -691,9 +700,10 @@ defmodule WandererAppWeb.MapAPIController do }} ] def character_activity(conn, params) do - with {:ok, map_id} <- Util.fetch_map_id(params) do - # Get raw activity data directly from the Map module instead of the Activity processor - raw_activity = WandererApp.Map.get_character_activity(map_id) + with {:ok, map_id} <- Util.fetch_map_id(params), + {:ok, days} <- parse_days(params["days"]) do + # Get raw activity data (filtered by days if provided, otherwise all activity) + raw_activity = WandererApp.Map.get_character_activity(map_id, days) # Group activities by user_id and summarize summarized_result = @@ -744,6 +754,15 @@ defmodule WandererAppWeb.MapAPIController do end end + # Parse days parameter, return nil if not provided to show all activity + defp parse_days(nil), do: {:ok, nil} + defp parse_days(days_str) do + case Integer.parse(days_str) do + {days, ""} when days > 0 -> {:ok, days} + _ -> {:ok, nil} # Return nil if invalid to show all activity + end + end + # If hours_str is present and valid, parse it. Otherwise return nil (no filter). defp parse_hours_ago(nil), do: nil defp parse_hours_ago(hours_str) do diff --git a/priv/posts/2025/03-05-api.md b/priv/posts/2025/03-05-api.md index eb8e898c..70523782 100644 --- a/priv/posts/2025/03-05-api.md +++ b/priv/posts/2025/03-05-api.md @@ -338,6 +338,7 @@ curl -H "Authorization: Bearer " \ ```bash GET /api/map/character-activity?map_id= GET /api/map/character-activity?slug= +GET /api/map/character-activity?map_id=&days=7 ``` - **Description:** Retrieves character activity data for a map, including passages, connections, and signatures. @@ -345,12 +346,13 @@ GET /api/map/character-activity?slug= - **Parameters:** - `map_id` (optional if `slug` is provided) — the UUID of the map. - `slug` (optional if `map_id` is provided) — the slug identifier of the map. + - `days` (optional) — if provided, filters activity data to only include records from the specified number of days. If not provided, returns all activity history. #### Example Request ```bash curl -H "Authorization: Bearer " \ - "https://wanderer.example.com/api/map/character-activity?slug=some-slug" + "https://wanderer.example.com/api/map/character-activity?slug=some-slug&days=7" ``` #### Example Response @@ -859,7 +861,7 @@ curl -X DELETE \ { "ok": true } ``` ---- +---- ## Conclusion @@ -876,9 +878,9 @@ For the most up-to-date and interactive documentation, we recommend using the Sw If you have any questions or need assistance with the API, please reach out to the Wanderer Team. ---- +---- -Fly safe, +Fly safe, **The Wanderer Team** ---- +---- \ No newline at end of file