Merge branch 'feature/related_events' into 'master'

Feature/related events

See merge request framasoft/mobilizon!113
This commit is contained in:
Thomas Citharel
2019-04-23 11:31:20 +02:00
13 changed files with 267 additions and 41 deletions

View File

@@ -45,6 +45,35 @@ 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],
limit: 1,
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(
@@ -231,18 +260,42 @@ defmodule Mobilizon.Events do
[%Event{}, ...]
"""
def list_events(page \\ nil, limit \\ nil) do
@spec list_events(integer(), integer(), atom(), atom()) :: list(Event.t())
def list_events(
page \\ nil,
limit \\ nil,
sort \\ :begins_on,
direction \\ :asc,
unlisted \\ false,
future \\ true
) do
query =
from(
e in Event,
where: e.visibility == ^:public,
preload: [:organizer_actor, :participants]
)
|> paginate(page, limit)
|> sort(sort, direction)
|> restrict_future_events(future)
|> allow_unlisted(unlisted)
Repo.all(query)
end
# Make sure we only show future events
@spec restrict_future_events(Ecto.Query.t(), boolean()) :: Ecto.Query.t()
defp restrict_future_events(query, true),
do: from(q in query, where: q.begins_on > ^DateTime.utc_now())
defp restrict_future_events(query, false), do: query
# Make sure unlisted events don't show up where they're not allowed
@spec allow_unlisted(Ecto.Query.t(), boolean()) :: Ecto.Query.t()
defp allow_unlisted(query, true),
do: from(q in query, where: q.visibility in [^:public, ^:unlisted])
defp allow_unlisted(query, false), do: from(q in query, where: q.visibility == ^:public)
@doc """
Find events by name
"""
@@ -276,6 +329,28 @@ defmodule Mobilizon.Events do
%{total: Task.await(total), elements: Task.await(elements)}
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.

View File

@@ -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,52 @@ 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, uuid: uuid},
_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)
# We find similar events with the same tags
# 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
))
|> 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)
{:ok, events}
end
defp uniq_events(events), do: Enum.uniq_by(events, fn event -> event.uuid end)
@doc """
Join an event for an actor
"""

View File

@@ -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))