Implement related events
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
20a4f7244c
commit
a877e4d7d9
|
@ -45,6 +45,34 @@ defmodule Mobilizon.Events do
|
|||
{:ok, events, count_events}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get an actor's eventual upcoming public event
|
||||
"""
|
||||
@spec get_actor_upcoming_public_event(Actor.t(), String.t()) :: Event.t() | nil
|
||||
def get_actor_upcoming_public_event(%Actor{id: actor_id} = _actor, not_event_uuid \\ nil) do
|
||||
query =
|
||||
from(
|
||||
e in Event,
|
||||
where:
|
||||
e.organizer_actor_id == ^actor_id and e.visibility in [^:public, ^:unlisted] and
|
||||
e.begins_on > ^DateTime.utc_now(),
|
||||
order_by: [asc: :begins_on],
|
||||
preload: [
|
||||
:organizer_actor,
|
||||
:tags,
|
||||
:participants,
|
||||
:physical_address
|
||||
]
|
||||
)
|
||||
|
||||
query =
|
||||
if is_nil(not_event_uuid),
|
||||
do: query,
|
||||
else: from(q in query, where: q.uuid != ^not_event_uuid)
|
||||
|
||||
Repo.one(query)
|
||||
end
|
||||
|
||||
def count_local_events do
|
||||
Repo.one(
|
||||
from(
|
||||
|
@ -274,6 +302,28 @@ defmodule Mobilizon.Events do
|
|||
Repo.all(query)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Find events with the same tags
|
||||
"""
|
||||
@spec find_similar_events_by_common_tags(list(), integer()) :: {:ok, list(Event.t())}
|
||||
def find_similar_events_by_common_tags(tags, limit \\ 2) do
|
||||
tags_ids = Enum.map(tags, & &1.id)
|
||||
|
||||
query =
|
||||
from(e in Event,
|
||||
distinct: e.uuid,
|
||||
join: te in "events_tags",
|
||||
on: e.id == te.event_id,
|
||||
where: e.begins_on > ^DateTime.utc_now(),
|
||||
where: e.visibility in [^:public, ^:unlisted],
|
||||
where: te.tag_id in ^tags_ids,
|
||||
order_by: [asc: e.begins_on],
|
||||
limit: ^limit
|
||||
)
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a event.
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ defmodule MobilizonWeb.Resolvers.Event do
|
|||
Handles the event-related GraphQL calls
|
||||
"""
|
||||
alias Mobilizon.Activity
|
||||
alias Mobilizon.Events
|
||||
alias Mobilizon.Events.{Event, Participant}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Users.User
|
||||
|
||||
# We limit the max number of events that can be retrieved
|
||||
@event_max_limit 100
|
||||
@number_of_related_events 3
|
||||
|
||||
def list_events(_parent, %{page: page, limit: limit}, _resolution)
|
||||
when limit < @event_max_limit do
|
||||
|
@ -43,6 +45,50 @@ defmodule MobilizonWeb.Resolvers.Event do
|
|||
{:ok, Mobilizon.Events.list_participants_for_event(uuid, 1, 10)}
|
||||
end
|
||||
|
||||
@doc """
|
||||
List related events
|
||||
"""
|
||||
def list_related_events(
|
||||
%Event{tags: tags, organizer_actor: organizer_actor},
|
||||
_args,
|
||||
_resolution
|
||||
) do
|
||||
# We get the organizer's next public event
|
||||
events =
|
||||
[Events.get_actor_upcoming_public_event(organizer_actor, uuid)] |> Enum.filter(&is_map/1)
|
||||
|
||||
# uniq_by : It's possible event_from_same_actor is inside events_from_tags
|
||||
events =
|
||||
(events ++
|
||||
Events.find_similar_events_by_common_tags(
|
||||
tags,
|
||||
@number_of_related_events - length(events)
|
||||
))
|
||||
|> uniq_events()
|
||||
|
||||
# TODO: We should use tag_relations to find more appropriate events
|
||||
|
||||
# We've considered all recommended events, so we fetch the latest events
|
||||
events =
|
||||
if @number_of_related_events - length(events) > 0 do
|
||||
(events ++
|
||||
Events.list_events(1, @number_of_related_events, :begins_on, :asc, true, true))
|
||||
|> uniq_events()
|
||||
else
|
||||
events
|
||||
end
|
||||
|
||||
events =
|
||||
events
|
||||
# We remove the same event from the results
|
||||
|> Enum.filter(fn event -> event.uuid != uuid end)
|
||||
# We return only @number_of_related_events right now
|
||||
|> Enum.take(@number_of_related_events)
|
||||
|
||||
# TODO: We should use tag_relations to find more events
|
||||
{:ok, events}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Join an event for an actor
|
||||
"""
|
||||
|
|
|
@ -56,6 +56,11 @@ defmodule MobilizonWeb.Schema.EventType do
|
|||
description: "The event's participants"
|
||||
)
|
||||
|
||||
field(:related_events, list_of(:event),
|
||||
resolve: &MobilizonWeb.Resolvers.Event.list_related_events/3,
|
||||
description: "Events related to this one"
|
||||
)
|
||||
|
||||
# field(:tracks, list_of(:track))
|
||||
# field(:sessions, list_of(:session))
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
defmodule Mobilizon.Repo.Migrations.EventEventTagOnDelete do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
drop(constraint(:events_tags, "events_tags_event_id_fkey"))
|
||||
drop(constraint(:events_tags, "events_tags_tag_id_fkey"))
|
||||
|
||||
alter table(:events_tags) do
|
||||
modify(:event_id, references(:events, on_delete: :delete_all))
|
||||
modify(:tag_id, references(:tags, on_delete: :delete_all))
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
drop(constraint(:events_tags, "events_tags_event_id_fkey"))
|
||||
drop(constraint(:events_tags, "events_tags_tag_id_fkey"))
|
||||
|
||||
alter table(:events_tags) do
|
||||
modify(:event_id, references(:events))
|
||||
modify(:tag_id, references(:tags))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20,7 +20,7 @@ defmodule Mobilizon.EventsTest do
|
|||
|
||||
setup do
|
||||
actor = insert(:actor)
|
||||
event = insert(:event, organizer_actor: actor)
|
||||
event = insert(:event, organizer_actor: actor, visibility: :public)
|
||||
{:ok, actor: actor, event: event}
|
||||
end
|
||||
|
||||
|
|
|
@ -322,5 +322,59 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do
|
|||
|
||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "cannot delete"
|
||||
end
|
||||
|
||||
test "list_related_events/3 should give related events", %{
|
||||
conn: conn,
|
||||
actor: actor
|
||||
} do
|
||||
tag1 = insert(:tag, title: "Elixir", slug: "elixir")
|
||||
tag2 = insert(:tag, title: "PostgreSQL", slug: "postgresql")
|
||||
|
||||
event = insert(:event, title: "Initial event", organizer_actor: actor, tags: [tag1, tag2])
|
||||
|
||||
event2 =
|
||||
insert(:event,
|
||||
title: "Event from same actor",
|
||||
organizer_actor: actor,
|
||||
visibility: :public,
|
||||
begins_on: Timex.shift(DateTime.utc_now(), days: 3)
|
||||
)
|
||||
|
||||
event3 =
|
||||
insert(:event,
|
||||
title: "Event with same tags",
|
||||
tags: [tag1, tag2],
|
||||
visibility: :public,
|
||||
begins_on: Timex.shift(DateTime.utc_now(), days: 3)
|
||||
)
|
||||
|
||||
query = """
|
||||
{
|
||||
event(uuid: "#{event.uuid}") {
|
||||
uuid,
|
||||
title,
|
||||
tags {
|
||||
id
|
||||
},
|
||||
related_events {
|
||||
uuid,
|
||||
title,
|
||||
tags {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "event"))
|
||||
|
||||
assert hd(json_response(res, 200)["data"]["event"]["related_events"])["uuid"] == event2.uuid
|
||||
|
||||
assert hd(tl(json_response(res, 200)["data"]["event"]["related_events"]))["uuid"] ==
|
||||
event3.uuid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -95,7 +95,7 @@ defmodule Mobilizon.Factory do
|
|||
|
||||
def event_factory do
|
||||
actor = build(:actor)
|
||||
start = Timex.now()
|
||||
start = Timex.shift(DateTime.utc_now(), hours: 2)
|
||||
uuid = Ecto.UUID.generate()
|
||||
|
||||
%Mobilizon.Events.Event{
|
||||
|
@ -108,6 +108,7 @@ defmodule Mobilizon.Factory do
|
|||
category: sequence("something"),
|
||||
physical_address: build(:address),
|
||||
visibility: :public,
|
||||
tags: build_list(3, :tag),
|
||||
url: "#{actor.url}/#{uuid}",
|
||||
uuid: uuid
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue