From 44a8e2ecc74a80416f26761dcb112128535316bf Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 11 Sep 2019 16:37:30 +0200 Subject: [PATCH] Show number of participants Signed-off-by: Thomas Citharel --- js/src/graphql/event.ts | 4 ++ js/src/types/event.model.ts | 5 ++ js/src/views/Event/Event.vue | 20 ++++++ lib/mobilizon/events/events.ex | 22 ++++++ lib/mobilizon_web/resolvers/event.ex | 10 ++- lib/mobilizon_web/schema/event.ex | 7 ++ schema.graphql | 11 ++- .../resolvers/participant_resolver_test.exs | 70 ++++++++++++++++++- 8 files changed, 146 insertions(+), 3 deletions(-) diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts index d6e98cff7..791339dc6 100644 --- a/js/src/graphql/event.ts +++ b/js/src/graphql/event.ts @@ -95,6 +95,10 @@ export const FETCH_EVENT = gql` participants { ${participantQuery} }, + participantStats { + approved, + unapproved + }, tags { ${tagsQuery} }, diff --git a/js/src/types/event.model.ts b/js/src/types/event.model.ts index f83e91e94..b6a4d53c6 100644 --- a/js/src/types/event.model.ts +++ b/js/src/types/event.model.ts @@ -92,6 +92,10 @@ export interface IEvent { organizerActor?: IActor; attributedTo: IActor; + participantStats: { + approved: number; + unapproved: number; + }; participants: IParticipant[]; relatedEvents: IEvent[]; @@ -154,6 +158,7 @@ export class EventModel implements IEvent { publishAt = new Date(); + participantStats = { approved: 0, unapproved: 0}; participants: IParticipant[] = []; relatedEvents: IEvent[] = []; diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue index 0faf56c42..82346453f 100644 --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -18,6 +18,25 @@

{{ event.title }}

+ + + One person is going + + + + + You and one other person are going to this event + + + + You're the only one going to this event +
@@ -406,6 +425,7 @@ export default class Event extends Vue { event.participants = event.participants .filter(p => p.actor.id !== data.leaveEvent.actor.id); + event.participantStats.approved = event.participantStats.approved - 1; store.writeQuery({ query: FETCH_EVENT, data: { event } }); }, diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex index cc6d71702..33521d4cc 100644 --- a/lib/mobilizon/events/events.ex +++ b/lib/mobilizon/events/events.ex @@ -747,6 +747,28 @@ defmodule Mobilizon.Events do |> paginate(page, limit) end + def count_approved_participants(id) do + query = + from( + p in Participant, + where: p.role != ^:not_approved, + where: p.event_id == ^id + ) + + Repo.aggregate(query, :count, :id) + end + + def count_unapproved_participants(id) do + query = + from( + p in Participant, + where: p.role == ^:not_approved, + where: p.event_id == ^id + ) + + Repo.aggregate(query, :count, :id) + end + @doc """ Returns the list of participations for an actor. diff --git a/lib/mobilizon_web/resolvers/event.ex b/lib/mobilizon_web/resolvers/event.ex index d5c4734fd..ed6a9aa45 100644 --- a/lib/mobilizon_web/resolvers/event.ex +++ b/lib/mobilizon_web/resolvers/event.ex @@ -6,7 +6,7 @@ defmodule MobilizonWeb.Resolvers.Event do alias Mobilizon.Addresses alias Mobilizon.Addresses.Address alias Mobilizon.Events - alias Mobilizon.Events.{Event, Participant} + alias Mobilizon.Events.{Event, Participant, EventOptions} alias Mobilizon.Media.Picture alias Mobilizon.Users.User alias Mobilizon.Actors @@ -51,6 +51,14 @@ defmodule MobilizonWeb.Resolvers.Event do {:ok, Mobilizon.Events.list_participants_for_event(uuid, 1, 10)} end + def stats_participants_for_event(%Event{id: id}, _args, _resolution) do + {:ok, + %{ + approved: Mobilizon.Events.count_approved_participants(id), + unapproved: Mobilizon.Events.count_unapproved_participants(id) + }} + end + @doc """ List related events """ diff --git a/lib/mobilizon_web/schema/event.ex b/lib/mobilizon_web/schema/event.ex index c2f2fbfe3..5190384d9 100644 --- a/lib/mobilizon_web/schema/event.ex +++ b/lib/mobilizon_web/schema/event.ex @@ -54,6 +54,8 @@ defmodule MobilizonWeb.Schema.EventType do field(:category, :string, description: "The event's category") + field(:participant_stats, :participant_stats, resolve: &Event.stats_participants_for_event/3) + field(:participants, list_of(:participant), resolve: &Event.list_participants_for_event/3, description: "The event's participants" @@ -92,6 +94,11 @@ defmodule MobilizonWeb.Schema.EventType do value(:cancelled, description: "The event is cancelled") end + object :participant_stats do + field(:approved, :integer, description: "The number of approved participants") + field(:unapproved, :integer, description: "The number of unapproved participants") + end + object :event_offer do field(:price, :float, description: "The price amount for this offer") field(:price_currency, :string, description: "The currency for this price offer") diff --git a/schema.graphql b/schema.graphql index df2395faf..6cc837e60 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,5 +1,5 @@ # source: http://localhost:4000/api -# timestamp: Mon Sep 09 2019 20:33:17 GMT+0200 (GMT+02:00) +# timestamp: Wed Sep 11 2019 11:53:12 GMT+0200 (GMT+02:00) schema { query: RootQueryType @@ -280,6 +280,7 @@ type Event implements ActionLogObject { """The event's organizer (as a person)""" organizerActor: Actor + participantStats: ParticipantStats """The event's participants""" participants: [Participant] @@ -648,6 +649,14 @@ type Participant { role: Int } +type ParticipantStats { + """The number of approved participants""" + approved: Int + + """The number of unapproved participants""" + unapproved: Int +} + """ Represents a person identity diff --git a/test/mobilizon_web/resolvers/participant_resolver_test.exs b/test/mobilizon_web/resolvers/participant_resolver_test.exs index dc2b287fb..046199338 100644 --- a/test/mobilizon_web/resolvers/participant_resolver_test.exs +++ b/test/mobilizon_web/resolvers/participant_resolver_test.exs @@ -12,7 +12,8 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do |> DateTime.truncate(:second), uuid: "b5126423-f1af-43e4-a923-002a03003ba4", url: "some url", - category: "meeting" + category: "meeting", + options: %{} } setup %{conn: conn} do @@ -387,5 +388,72 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do } ] end + + test "stats_participants_for_event/3 give the number of (un)approved participants", %{ + conn: conn, + actor: actor + } do + event = + @event + |> Map.put(:organizer_actor_id, actor.id) + + {:ok, event} = Events.create_event(event) + + query = """ + { + event(uuid: "#{event.uuid}") { + uuid, + participantStats { + approved, + unapproved + } + } + } + """ + + res = + conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + + assert json_response(res, 200)["data"]["event"]["uuid"] == to_string(event.uuid) + assert json_response(res, 200)["data"]["event"]["participantStats"]["approved"] == 1 + assert json_response(res, 200)["data"]["event"]["participantStats"]["unapproved"] == 0 + + moderator = insert(:actor) + + Events.create_participant(%{ + role: :moderator, + event_id: event.id, + actor_id: moderator.id + }) + + unapproved = insert(:actor) + + Events.create_participant(%{ + role: :not_approved, + event_id: event.id, + actor_id: unapproved.id + }) + + query = """ + { + event(uuid: "#{event.uuid}") { + uuid, + participantStats { + approved, + unapproved + } + } + } + """ + + res = + conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + + assert json_response(res, 200)["data"]["event"]["uuid"] == to_string(event.uuid) + assert json_response(res, 200)["data"]["event"]["participantStats"]["approved"] == 2 + assert json_response(res, 200)["data"]["event"]["participantStats"]["unapproved"] == 1 + end end end