mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-05 23:35:33 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de6205f860 | ||
|
|
f994255091 | ||
|
|
6d4981a3db | ||
|
|
06fef2296f |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -2,6 +2,19 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.58.0](https://github.com/wanderer-industries/wanderer/compare/v1.57.1...v1.58.0) (2025-03-22)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Show online state on map characters page
|
||||
|
||||
* api: update character activity and api to allow date range (#299)
|
||||
|
||||
* api: update character activity and api to allow date range
|
||||
|
||||
## [v1.57.1](https://github.com/wanderer-industries/wanderer/compare/v1.57.0...v1.57.1) (2025-03-20)
|
||||
|
||||
|
||||
|
||||
@@ -401,12 +401,17 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
defp _get_routes(origin, destination, params, opts),
|
||||
do: _get_routes_eve(origin, destination, params, opts)
|
||||
|
||||
defp _get_routes_eve(origin, destination, params, opts),
|
||||
do:
|
||||
get(
|
||||
"/route/#{origin}/#{destination}/?#{params |> Plug.Conn.Query.encode()}",
|
||||
opts
|
||||
)
|
||||
defp _get_routes_eve(origin, destination, params, opts) do
|
||||
esi_params = Map.merge(params, %{
|
||||
connections: params.connections |> Enum.join(","),
|
||||
avoid: params.avoid |> Enum.join(",")
|
||||
})
|
||||
|
||||
get(
|
||||
"/route/#{origin}/#{destination}/?#{esi_params |> Plug.Conn.Query.encode()}",
|
||||
opts
|
||||
)
|
||||
end
|
||||
|
||||
defp get_auth_opts(opts), do: [auth: {:bearer, opts[:access_token]}]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -54,14 +54,20 @@ defmodule WandererAppWeb.MapCharacters do
|
||||
>
|
||||
Tracked
|
||||
</span>
|
||||
<span :if={is_online?(@character.id)} class="text-green-500 rounded-full px-2 py-1">
|
||||
Online
|
||||
</span>
|
||||
<span :if={not is_online?(@character.id)} class="text-red-500 rounded-full px-2 py-1">
|
||||
Offline
|
||||
</span>
|
||||
<div class="avatar">
|
||||
<div class="rounded-md w-8 h-8">
|
||||
<img src={member_icon_url(@character.eve_id)} alt={@character.name} />
|
||||
</div>
|
||||
</div>
|
||||
<span><%= @character.name %></span>
|
||||
<span :if={@character.alliance_ticker}>[<%= @character.alliance_ticker %>]</span>
|
||||
<span :if={@character.corporation_ticker}>[<%= @character.corporation_ticker %>]</span>
|
||||
<span>{@character.name}</span>
|
||||
<span :if={@character.alliance_ticker}>[{@character.alliance_ticker}]</span>
|
||||
<span :if={@character.corporation_ticker}>[{@character.corporation_ticker}]</span>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
@@ -79,4 +85,8 @@ defmodule WandererAppWeb.MapCharacters do
|
||||
end)
|
||||
end
|
||||
|
||||
defp is_online?(character_id) do
|
||||
{:ok, state} = WandererApp.Character.get_character_state(character_id)
|
||||
state.is_online
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
id="map-character-list"
|
||||
class="pt-20 w-full h-full col-span-2 lg:col-span-1 p-4 pl-20 pb-20 overflow-auto"
|
||||
>
|
||||
|
||||
<div class="mb-6 p-4 border rounded-md flex gap-2 items-center">
|
||||
<.icon name="hero-information-circle-mini" class="h-5 w-5" />
|
||||
<p>
|
||||
'Untrack' characters leading to completely remove them from the map, required manually enable the tracking by users later.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<.live_component
|
||||
module={MapCharacters}
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
|
||||
@version "1.57.1"
|
||||
@version "1.58.0"
|
||||
|
||||
def project 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,
|
||||
Fly safe,
|
||||
**The Wanderer Team**
|
||||
|
||||
---
|
||||
----
|
||||
Reference in New Issue
Block a user