mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 18:56:01 +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 """
|
@doc """
|
||||||
Returns the raw activity data that can be processed by WandererApp.Character.Activity.
|
Returns the raw activity data that can be processed by WandererApp.Character.Activity.
|
||||||
Only includes characters that are on the map's ACL.
|
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)
|
{:ok, map} = WandererApp.Api.Map.by_id(map_id)
|
||||||
_map_with_acls = Ash.load!(map, :acls)
|
_map_with_acls = Ash.load!(map, :acls)
|
||||||
|
|
||||||
{:ok, jumps} = WandererApp.Api.MapChainPassages.by_map_id(%{map_id: map_id})
|
# Calculate cutoff date if days is provided
|
||||||
thirty_days_ago = DateTime.utc_now() |> DateTime.add(-30 * 24 * 3600, :second)
|
cutoff_date = if days, do: DateTime.utc_now() |> DateTime.add(-days * 24 * 3600, :second), else: nil
|
||||||
|
|
||||||
# Get activity data
|
# Get activity data
|
||||||
connections_activity = get_connections_activity(map_id, thirty_days_ago)
|
passages_activity = get_passages_activity(map_id, cutoff_date)
|
||||||
signatures_activity = get_signatures_activity(map_id, thirty_days_ago)
|
connections_activity = get_connections_activity(map_id, cutoff_date)
|
||||||
|
signatures_activity = get_signatures_activity(map_id, cutoff_date)
|
||||||
|
|
||||||
# Return raw activity data
|
# Return activity data
|
||||||
jumps
|
passages_activity
|
||||||
|> Enum.map(fn passage ->
|
|> Enum.map(fn passage ->
|
||||||
%{
|
%{
|
||||||
character: passage.character,
|
character: passage.character,
|
||||||
@@ -554,14 +556,40 @@ defmodule WandererApp.Map do
|
|||||||
end)
|
end)
|
||||||
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,
|
from(ua in WandererApp.Api.UserActivity,
|
||||||
join: c in assoc(ua, :character),
|
join: c in assoc(ua, :character),
|
||||||
where:
|
where:
|
||||||
ua.entity_id == ^map_id and
|
ua.entity_id == ^map_id and
|
||||||
ua.entity_type == :map and
|
ua.entity_type == :map and
|
||||||
ua.event_type == :map_connection_added and
|
ua.event_type == :map_connection_added,
|
||||||
ua.inserted_at > ^thirty_days_ago,
|
|
||||||
group_by: [c.id],
|
group_by: [c.id],
|
||||||
select: {c.id, count(ua.id)}
|
select: {c.id, count(ua.id)}
|
||||||
)
|
)
|
||||||
@@ -569,14 +597,43 @@ defmodule WandererApp.Map do
|
|||||||
|> Map.new()
|
|> Map.new()
|
||||||
end
|
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,
|
from(ua in WandererApp.Api.UserActivity,
|
||||||
join: c in assoc(ua, :character),
|
join: c in assoc(ua, :character),
|
||||||
where:
|
where:
|
||||||
ua.entity_id == ^map_id and
|
ua.entity_id == ^map_id and
|
||||||
ua.entity_type == :map and
|
ua.entity_type == :map and
|
||||||
ua.event_type == :signatures_added 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}
|
select: {ua.character_id, ua.event_data}
|
||||||
)
|
)
|
||||||
|> WandererApp.Repo.all()
|
|> WandererApp.Repo.all()
|
||||||
|
|||||||
@@ -648,15 +648,17 @@ defmodule WandererAppWeb.MapAPIController do
|
|||||||
Returns character activity data for a map.
|
Returns character activity data for a map.
|
||||||
|
|
||||||
Requires either `?map_id=<UUID>` or `?slug=<map-slug>`.
|
Requires either `?map_id=<UUID>` or `?slug=<map-slug>`.
|
||||||
|
Optional `days` parameter to filter activity to a specific time period.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
GET /api/map/character_activity?map_id=<uuid>
|
GET /api/map/character_activity?map_id=<uuid>
|
||||||
GET /api/map/character_activity?slug=<map-slug>
|
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()
|
@spec character_activity(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||||
operation :character_activity,
|
operation :character_activity,
|
||||||
summary: "Get 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: [
|
parameters: [
|
||||||
map_id: [
|
map_id: [
|
||||||
in: :query,
|
in: :query,
|
||||||
@@ -671,6 +673,13 @@ defmodule WandererAppWeb.MapAPIController do
|
|||||||
type: :string,
|
type: :string,
|
||||||
required: false,
|
required: false,
|
||||||
example: "map-name"
|
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: [
|
responses: [
|
||||||
@@ -691,9 +700,10 @@ defmodule WandererAppWeb.MapAPIController do
|
|||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
def character_activity(conn, params) do
|
def character_activity(conn, params) do
|
||||||
with {:ok, map_id} <- Util.fetch_map_id(params) do
|
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||||
# Get raw activity data directly from the Map module instead of the Activity processor
|
{:ok, days} <- parse_days(params["days"]) do
|
||||||
raw_activity = WandererApp.Map.get_character_activity(map_id)
|
# 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
|
# Group activities by user_id and summarize
|
||||||
summarized_result =
|
summarized_result =
|
||||||
@@ -744,6 +754,15 @@ defmodule WandererAppWeb.MapAPIController do
|
|||||||
end
|
end
|
||||||
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).
|
# 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(nil), do: nil
|
||||||
defp parse_hours_ago(hours_str) do
|
defp parse_hours_ago(hours_str) do
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
|||||||
```bash
|
```bash
|
||||||
GET /api/map/character-activity?map_id=<UUID>
|
GET /api/map/character-activity?map_id=<UUID>
|
||||||
GET /api/map/character-activity?slug=<map-slug>
|
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.
|
- **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:**
|
- **Parameters:**
|
||||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
- `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.
|
- `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
|
#### Example Request
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
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
|
#### Example Response
|
||||||
@@ -859,7 +861,7 @@ curl -X DELETE \
|
|||||||
{ "ok": true }
|
{ "ok": true }
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
----
|
||||||
|
|
||||||
## Conclusion
|
## 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.
|
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**
|
**The Wanderer Team**
|
||||||
|
|
||||||
---
|
----
|
||||||
Reference in New Issue
Block a user