mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-13 11:15:51 +00:00
feat (api): update character activity and api to allow date range (#299)
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
* feat (api): update character activity and api to allow date range
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -648,15 +648,17 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
Returns character activity data for a map.
|
||||
|
||||
Requires either `?map_id=<UUID>` or `?slug=<map-slug>`.
|
||||
Optional `days` parameter to filter activity to a specific time period.
|
||||
|
||||
Example:
|
||||
GET /api/map/character_activity?map_id=<uuid>
|
||||
GET /api/map/character_activity?slug=<map-slug>
|
||||
GET /api/map/character_activity?map_id=<uuid>&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
|
||||
|
||||
@@ -338,6 +338,7 @@ curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
```bash
|
||||
GET /api/map/character-activity?map_id=<UUID>
|
||||
GET /api/map/character-activity?slug=<map-slug>
|
||||
GET /api/map/character-activity?map_id=<UUID>&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=<map-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 <REDACTED_TOKEN>" \
|
||||
"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,
|
||||
**The Wanderer Team**
|
||||
|
||||
---
|
||||
----
|
||||
Reference in New Issue
Block a user