eventmanager

View readme on GitLab

frontend/email/email.proto

syntax = "proto3";

package eventmanager.frontend.email;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/email";

import "eventmanager/frontend/person/person.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

// The email service allows for creating, managing and sending mails to event
// participants and helpers.
service EmailService {
  // Gets a mail
  rpc GetMail(GetMailRequest) returns(Mail) {}
  // Creates a mail
  rpc CreateMail(CreateMailRequest) returns(Mail) {}
  // Updates a mail
  rpc UpdateMail(UpdateMailRequest) returns(Mail) {}
  // Deletes a mail
  rpc DeleteMail(DeleteMailRequest) returns(google.protobuf.Empty) {}
  // Lists a set of of mails
  rpc ListMails(ListMailsRequest) returns(MailList) {}

  // Resets a status mail to default
  rpc ResetToDefault(ResetToDefaultRequest) returns(Mail) {}

  // Sets mail type to 'to-review'
  rpc SetMailReviewable(SetMailReviewableRequest) returns(Mail) {}
  // Accept mail review
  rpc AcceptMailReview(AcceptMailReviewRequest) returns(Mail) {}
  // Reject mail review and push new mail version
  rpc RejectMailReview(RejectMailReviewRequest) returns(Mail) {}

  // Send manual email
  rpc SendEmail(SendEmailRequest) returns(Mail) {}

  // Gets the mail content default for a certain status
  rpc GetStatusMailDefault(GetStatusMailDefaultRequest)
      returns(StatusMailDefault) {}
  // Creates the status mail default
  rpc CreateStatusMailDefault(CreateStatusMailDefaultRequest)
      returns(StatusMailDefault) {}
  // Lists all existing status mail defaults
  rpc ListStatusMailDefaults(google.protobuf.Empty)
      returns(ListStatusMailDefaultsResponse) {}
  // Updates the status mail default
  rpc UpdateStatusMailDefault(UpdateStatusMailDefaultRequest)
      returns(StatusMailDefault) {}
  // Deletes the status mail default
  rpc DeleteStatusMailDefault(DeleteStatusMailDefaultRequest)
      returns(google.protobuf.Empty) {}

  // Gets the mail content preview for the mail template
  rpc GetMailContentPreview(GetMailContentPreviewRequest) returns(MailContent) {
  }
}

message GetMailRequest { string id = 1; }

message CreateMailRequest { Mail mail = 1; }
message UpdateMailRequest { Mail mail = 1; }
message DeleteMailRequest { string id = 1; }

message ListMailsRequest {
  // which mails to return
  message MailFilter {
    // ids of the events
    repeated string event_ids = 1;
    // address group type
    repeated AddressGroupEnum address_groups = 2;
    // type of the mail entry
    repeated MailTypeEnum types = 3;
    // review stage of mail
    repeated MailReviewStageEnum stages = 4;
    // substring search in mail text
    string text = 5;
    // reviewable mails only
    bool reviewable_only = 6;
  }
  MailFilter filter = 1;
  int32 page_size = 2;
  string page_token = 3;
}

message ResetToDefaultRequest { string id = 1; }

message SetMailReviewableRequest { string id = 1; }

message AcceptMailReviewRequest { string id = 1; }

message RejectMailReviewRequest { Mail mail = 1; }

message SendEmailRequest { string id = 1; }

message GetStatusMailDefaultRequest {
  StatusMailEnum status = 1;
  AddressGroupEnum group = 2;
}

message CreateStatusMailDefaultRequest {
  StatusMailDefault status_mail_default = 1;
}

message ListStatusMailDefaultsResponse {
  repeated StatusMailDefault defaults = 1;
}

message UpdateStatusMailDefaultRequest {
  StatusMailDefault status_mail_default = 1;
}

message DeleteStatusMailDefaultRequest { string id = 1; }

message GetMailContentPreviewRequest { Mail mail = 1; }

message StatusMailDefault {
  string id = 1;
  StatusMailEnum status = 2;
  MailContent content = 3;
  AddressGroupEnum group = 4;
}

message Mail {
  string id = 1;
  string event_id = 2;
  AddressGroupEnum group = 3;

  oneof type {
    StatusMail status_mail = 4;
    TimeTriggerMail time_trigger_mail = 5;
    ManualMail manual_mail = 6;
  }

  MailContent modified_content = 7;
  MailContent reviewed_content =
      8; // might not be set, if no reviewed version yet
  MailReviewStageEnum stage = 9;
  eventmanager.frontend.person.Person last_modifying_person = 10;
}

message MailList {
  repeated Mail mail = 1;
  string next_page_token = 2;
}

message MailContent {
  string title_de = 1;
  string title_en = 2;
  string text_de = 3;
  string text_en = 4;
}

message StatusMail { StatusMailEnum status = 1; }

message TimeTriggerMail {
  google.protobuf.Timestamp send_at = 1;
  google.protobuf.Timestamp sent_at = 2;
}

message ManualMail { google.protobuf.Timestamp sent_at = 1; }

// used for database to identify the type of the mail
enum MailTypeEnum {
  // Standard mails -> have review-process
  MANUAL_MAIL = 0;
  // Mails sent upon certain internal status changes -> have review-process
  STATUS_MAIL = 1;
  // Mails sent upon certain times -> have review process
  TIMED_MAIL = 2;
}

enum MailReviewStageEnum { MODIFIED = 0; REVIEWABLE = 1; ACCEPTED = 2; }

// StatusMailEnum defines all status changes that might provoke an email
enum StatusMailEnum {
  EVENT_PUBLISHED = 0; EVENT_REGISTRATION_OPEN = 1; EVENT_WAITING_LIST = 2;
  EVENT_GRAYLIST = 3;
  EVENT_REGISTRATION_CLOSED = 4;
  EVENT_ONGOING = 5;
  EVENT_PASSED = 6;
  PARTICIPATION_WAITLIST = 7;
  PARTICIPATION_PENDING_CONFIRMATION = 8;
  PARTICIPATION_CONFIRMED = 9;
  PARTICIPATION_DEREGISTERED = 10;
  PARTICIPATION_FEEDBACK = 11;
  PARTICIPATION_GRAYLIST = 12;
  PARTICIPATION_ATTENDED = 13;
  PARTICIPATION_MISSED_EVENT = 14;
  PARTICIPATION_MISSED_CONFIRMATION = 15;
  PARTICIPATION_CREATED = 16;
  HELPER_CREATED = 17;
  HELPER_FEEDBACK = 18;
}

// AddressGroupEnum defines the different address groups for which the mails can
// be sent
enum AddressGroupEnum {
  // All registered and confirmed people
  CONFIRMED_PEOPLE = 0;
  // All registered but not confirmed people
  REGISTERED_BUT_NOT_CONFIRMED_PEOPLE = 1;
  // All helpers for this event
  EVENT_HELPERS = 2;
  // All people with helper intent set
  ALL_HELPERS = 3;
  // All people from the waiting list
  WAITING_LIST = 4;
  // All graylisted people trying to sign up
  GRAYLIST = 5;
  // All Vis members (e.g. for event announcements)
  ALL_VIS = 6;
  // All active Vis members
  ALL_ACTIVE_VIS = 7;

}

frontend/event/event.proto

syntax = "proto3";

package eventmanager.frontend.event;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/event";

import "accounting-api/accounting.proto";
import "eventmanager/frontend/option/option.proto";
import "eventmanager/frontend/person/person.proto";
import "eventmanager/frontend/email/email.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";

// EventService allows managing events
service EventService {
  // Creates an event (initially only with editable version).
  rpc CreateEvent(CreateEventRequest) returns(Event) {}

  // Clones an event with new base date.
  rpc CloneEvent(CloneEventRequest) returns(Event) {}

  // Updates the editable version of an event. The edited version's status is
  // automatically set to "MODIFIED".
  // To update the public version, a section reviewed in the editable version
  // is automatically pushed to the public version
  rpc UpdateEvent(UpdateEventRequest) returns(Event) {}

  // Manually updates the state of the published event version.
  // Note that the state can only be forwarded.
  rpc UpdateEventState(UpdateEventStateRequest) returns(Event) {}

  // Updates the budget of an Event (note that the budget is the same for draft
  // and published version)
  rpc UpdateEventBudget(UpdateEventBudgetRequest) returns(Event) {}

  rpc ListEventBudgets(ListEventBudgetsRequest)
      returns(ListEventBudgetsResponse) {}

  // Sets sections of the modifiable version of the event to reviewable.
  rpc SetEventSectionsReviewable(SetEventSectionsReviewableRequest)
      returns(Event) {}

  // Accepts modified sections of an event
  // Anybody with access to the admin UI can do this (other than the person
  // who has submitted them for review)
  rpc AcceptEventSectionsChange(AcceptEventChangeRequest) returns(Event) {}

  // Rejects the changes done to the editable version
  // A rejection needs to include a change to the event to fix it
  rpc RejectEventChange(RejectEventChangeRequest) returns(Event) {}

  // Pushes all changes which do not need review to the published version
  // This excludes options and prices but includes event
  // accessability/visibility and transition dates other than start/end time
  rpc PushNoReviewEventChanges(PushNoReviewEventChangesRequest) returns(Event) {
  }

  // Pushes all changes made to the event options and prices to the published
  // version
  rpc PushEventOptionsAndCosts(PushNoReviewEventChangesRequest) returns(Event) {
  }

  // Gets event info
  rpc GetEvent(GetEventRequest) returns(Event) {}

  // Gets event link to public UI
  rpc GetPublicUILink(GetPublicUILinkRequest) returns(GetPublicUILinkResponse) {
  }

  // Gets the metadata of the event
  rpc GetEventMetadata(GetEventMetadataRequest) returns(EventMetadata) {}
  // Updates the metadata of the event (like organisers)
  rpc UpdateEventMetadata(UpdateEventMetadataRequest) returns(EventMetadata) {}

  // Deletes an event
  rpc DeleteEvent(DeleteEventRequest) returns(google.protobuf.Empty) {}

  // Lists all events (with given query parameters)
  rpc ListEvents(ListEventsRequest) returns(ListEventsResponse) {}

  // GetNextEventState returns for a given published event the logically next
  // state that the corresponding event can enter. Also, the next state for
  // archived events is again archived as it is the last state. The next state
  // is AT MOST ONE STEP down the state chain.
  rpc GetNextEventState(GetNextStateRequest) returns(GetNextStateResponse) {}

  // SetEventState sets the event to the requested state if its a possible
  // state. Possible states are returned by GetNextEventState. See
  // GetNextEventState for more details
  rpc SetEventState(SetEventStateRequest) returns(Event) {}

  // CreateEventCategory creates a new Event category
  rpc CreateEventCategory(CreateEventCategoryRequest) returns(EventCategory) {}

  // UpdateEventCategory updates an event category
  rpc UpdateEventCategory(UpdateEventCategoryRequest) returns(EventCategory) {}

  // DeleteEventCategory deletes an event category
  rpc DeleteEventCategory(DeleteEventCategoryRequest)
      returns(google.protobuf.Empty) {}

  // ListEventCategories lists all event categories
  rpc ListEventCategories(ListEventCategoriesRequest)
      returns(ListEventCategoriesResponse) {}

  // CreateEventPoster compresses and stores an image file (png, jpeg or gif)
  rpc CreateEventPoster(CreateEventPosterRequest) returns(EventPoster) {}

  // GetEventPoster returns a URL to a poster accessible by a browser
  rpc GetEventPoster(GetEventPosterRequest) returns(EventPoster) {}

  // UpdateEventPoster updates the stored image
  rpc UpdateEventPoster(UpdateEventPosterRequest) returns(EventPoster) {}

  // DeleteEventPoster deletes the stored image and removes any references to it
  // from events
  rpc DeleteEventPoster(DeleteEventPosterRequest)
      returns(google.protobuf.Empty) {}
}

message CreateEventRequest {
  Event event = 1;
  EventMetadata metadata = 2;
}

message CloneEventRequest {
  string event_id = 1; // Id of the event that should be cloned
  eventmanager.frontend.person.Person main_organiser = 2;
  repeated eventmanager.frontend.person.Person coorganisers = 3;
  google.protobuf.Timestamp new_event_start_time = 4;
  string budget_id = 5;
}

message UpdateEventRequest { Event event = 1; }

message UpdateEventStateRequest {
  string id = 1;
  EventStati target_state = 2;
}

message UpdateEventBudgetRequest {
  string id = 1;
  string budget_id = 2;
}

message ListEventBudgetsRequest {}

message ListEventBudgetsResponse {
  repeated accounting.ProjectAccount accounts = 1;
}

message SetEventSectionsReviewableRequest {
  string id = 1;
  repeated ReviewSections sections = 2;
}

message AcceptEventChangeRequest {
  string id = 1;
  string version = 2; // The version one wants to accept
  repeated ReviewSections sections = 3;
}

message RejectEventChangeRequest {
  Event event = 1;
  repeated ReviewSections rejected_sections = 2;
}

message PushNoReviewEventChangesRequest {
  string id = 1;
  string version = 2;
}

message GetEventRequest {
  string id = 1;
  // Whether to fetch the reviewed version or the one under review/editing
  // (explained further on the Event message)
  bool editable_version = 2;
  string link_token = 3;
}

message GetPublicUILinkRequest { string id = 1; }

message GetPublicUILinkResponse { string url = 1; }

message DeleteEventRequest { string id = 1; }

message ListEventsRequest {
  // Which events to display
  message EventFilter {
    // Text for substring search in title (english and german)
    string title = 1;
    // Text for substring search in description (english and german)
    string description = 2;
    // IDs of categories to match
    repeated string category = 3;
    // Substring match in exclusion group
    string mutual_exclusion_group = 4;

    message CapacityFilter {
      // Match events with unlimited capacity
      bool unlimited = 1;
      // Match events with limited capacity of at least from people
      int32 from = 2;
      // Match events with limited capacity of at most until people
      int32 until = 3;
    }
    // Filter events for capacity limits
    CapacityFilter capacity = 5;

    // Open ranges for filtering event timestamps
    message DateRangeFilter {
      oneof from {
        bool unlimited_from_range = 1;
        google.protobuf.Timestamp timestamp_from = 2;
      }
      oneof until {
        bool unlimited_until_range = 3;
        google.protobuf.Timestamp timestamp_until = 4;
      }
    }
    // Filter time of publication
    DateRangeFilter publish_time = 6;
    // Filter time of registration opening
    DateRangeFilter registration_time = 7;
    // Filter graylist enlistment time
    DateRangeFilter graylist_time = 8;
    // Filter registration closing time
    DateRangeFilter registration_end_time = 9;
    // Filter latest confirm time
    DateRangeFilter latest_confirm_time = 10;
    // Filter event start time
    DateRangeFilter event_start_time = 11;
    // Filter event end time
    DateRangeFilter event_end_time = 12;

    // Which event states to match
    repeated EventStati stati = 13;
    // Which column in the database to sort by
    SortBy sort_by = 14;
    // Whether to sort by descending order
    bool descending = 15;
    // Allowed entrant sets
    repeated AllowedParticipants entrant_sets = 16;

    enum SortBy {
      // Date created
      CREATED = 0;
      // Last edited date
      LAST_EDITED = 1;
      // Date to publish event
      PUBLISH_DATE = 2;
      // Date registration opens
      REGISTER_DATE = 3;
      // Date to allow graylisted users to register
      GRAYLIST_DATE = 4;
      // Date registration closes
      REGISTRATION_CLOSE_DATE = 5;
      // Latest date to confirm participation
      LATEST_CONFIRM_DATE = 6;
      // Event start date
      EVENT_START = 7;
      // Event end date
      EVENT_END = 8;
      // Title german (lexicographic)
      TITLE_DE = 9;
      // Title english (lexicographic)
      TITLE_EN = 10;
      // Description german (lexicographic)
      DESCRIPTION_DE = 11;
      // Description english (lexicographic)
      DESCRIPTION_EN = 12;
      // Event category (lexicographic)
      CATEGORY = 13;
      // Mutual exclusion group (lexicographic)
      MUTUAL_EXCLUSION_GROUP = 14;
      // Event capacity (numerical)
      CAPACITY = 15;
    }

    // The person checking (to make sure only events visible to the person are
    // returned)
    eventmanager.frontend.person.Person person = 17;

    // Used to filter only for events which the current user may edit
    bool only_editable = 18;
  }
  EventFilter filter = 1;
  int32 page_size = 2;
  string page_token = 3;
  // Whether to fetch the reviewed version or the one under review/editing
  // (explained further on the Event message)
  bool editable_version = 4;
}

message ListEventsResponse {
  repeated Event events = 1;
  string next_page_token = 2;
}

message GetNextStateRequest { string event_id = 1; }

message GetNextStateResponse { EventStati status = 1; }

message SetEventStateRequest {
  string event_id = 1;
  EventStati next_state = 2;
}

message GetEventMetadataRequest { string event_id = 1; }

message UpdateEventMetadataRequest {
  string event_id = 1;
  EventMetadata metadata = 2;
}

message EventMetadata {
  // Main event organiser
  eventmanager.frontend.person.Person main_organiser = 1;
  // Event coorganisers
  repeated eventmanager.frontend.person.Person coorganisers = 2;

  // Free (markdown) text that is used to transfer knowdledge between events.
  string knowledge_transfer = 3;

  // Event whitelist (should event have whitelist enabled)
  repeated Whitelist whitelist = 32;
}

message CreateEventCategoryRequest { EventCategory category = 1; }

message UpdateEventCategoryRequest { EventCategory category = 1; }

message DeleteEventCategoryRequest { string id = 1; }

message ListEventCategoriesRequest {
  int32 page_size = 1;
  string page_token = 2;
}

message ListEventCategoriesResponse {
  repeated EventCategory categories = 1;
  string next_page_token = 2;
}

message EventCategory {
  string id = 1;
  string label_en = 2;
  string label_de = 3;
  bool hidden = 4;
}

message EventPoster {
  string id = 1;
  string access_url = 2;
}

message CreateEventPosterRequest { bytes data = 1; }

message UpdateEventPosterRequest {
  string id = 1;
  bytes data = 2;
}

message DeleteEventPosterRequest { string id = 1; }

message GetEventPosterRequest { string id = 1; }
/* Each event exists has two copies:
 *    - a public version
 *    - an editable version.
 *  The public version is the one visible as a user (though if it is not yet in
 *  the PUBLISHED state, it may not yet be displayed anywhere).
 *  Only boardmembers/organisers can view and change around the editable
 * version. This way a user can never see an unreviewed version of an event
 */
message Event {
  string id = 1;
  // True iff this is the editable version of the event
  // (as opposed to a reviewed)
  bool editable_version = 2;
  // Event title
  string title_de = 3;
  string title_en = 4;
  // Event teaser
  string teaser_de = 33;
  string teaser_en = 34;
  // Event description
  string description_de = 5;
  string description_en = 6;
  // Event category, from a predefined list which is defined in the admin UI
  // (for instance: food, company trip, etc.)
  string category = 7;
  // Number of people allowed in the event
  oneof capacity {
    bool unlimited = 8;
    int32 limited = 9;
  }

  EventStati status = 10;

  AllowedParticipants allowed_participants = 11;

  // Possible groups allowed as helpers for event
  enum AllowedHelpers {
    // Only VIS members are allowed to sign up as helper for this event
    MEMBERS = 0;
    // Any ETH member can sign up as helper for this event
    ANYBODY = 1;
  }

  AllowedHelpers allowed_helpers = 26;

  // Possible groups allowed to see the event
  enum EventVisibility {
    // Anyone can see the event
    PUBLIC = 0;
    // All people allowed to enter the event can see it
    ALLOWED = 1;
    // The event is private and visible via link only
    LINK_ONLY = 2;
  }

  EventVisibility visibility = 12;

  // All state transitions triggered by datetimes
  message StateTransitions {
    // Time of publication (can only trigger if event is REVIEWED)
    google.protobuf.Timestamp publish_time = 1;
    // Time of opening registration (only if PUBLIC)
    google.protobuf.Timestamp registration_time = 2;
    // From this point on, graylisted users are allowed to enlist
    google.protobuf.Timestamp graylist_time = 3;
    // Registration closes
    google.protobuf.Timestamp registration_end_time = 4;
    // If a user is not confirmed by this time, remove them from the event
    google.protobuf.Timestamp latest_confirm_time = 7;
    // Start of event
    google.protobuf.Timestamp event_start_time = 5;
    // End of event
    google.protobuf.Timestamp event_end_time = 6;
  }

  StateTransitions transitions = 13;

  // Describes all options
  repeated eventmanager.frontend.option.Option participation_options = 14;

  // Possible types of confirmation mechanism
  enum ConfirmationType {
    // Registring for the event does not need any additional confirmation
    AUTO_CONFIRM = 0;
    // In person confirmation (via POS) is required for event participation
    CONFIRM = 1;
    // Event fee needs to be paid (via POS) to participate in event
    PAYMENT = 2;
  }

  // Describes which type of confirmation is needed for the event
  ConfirmationType confirmation_type = 15;

  bool enable_payment_api = 37;

  // Timeframe in which a user can confirm their participation before being
  // removed from the event
  google.protobuf.Duration confirmation_time = 25;

  // Describes cost for all option configurations
  repeated ParticipationCost participation_costs = 16;

  // The version field prevents two editors to overwrite the version of the
  // other without warning. The version changes each time the event is updated.
  string version = 17;

  // Events can also be created without participant registration
  bool no_registration = 18;
  // Signup affected by a person's graylist status
  bool graylist_disabled = 19;
  // Disabling waitlist automatically closes registration when event is full
  bool waitlist_disabled = 20;
  // Not attending the event after having registered allows graylisting
  bool causes_graylist = 21;
  // Allow signup to this event even it overlaps with another event
  // the user has already signed up for (or vice versa)
  bool ignore_overlap = 22;

  // Optional identifier for a poster
  string poster_id = 23;

  // No user is allowed to participate in multiple events of the same
  // mutual exclusion group
  string mutual_exclusion_group = 24;

  // Review status for a part of an event
  message ReviewStatus {
    // The user which modified the section and submitted it for review
    person.Person modified_by = 1;
    // The user which accepted the change to the section
    person.Person reviewed_by = 2;
  }

  // German title and description review status
  ReviewStatus text_de_status = 27;
  // English title and description review status
  ReviewStatus text_en_status = 28;
  // Event date review status
  ReviewStatus event_date_status = 29;

  // The number of participations each state
  message ParticipationCounts {
    // Number of confirmed participants
    int32 confirmed = 1;
    // Number of participants who haven't confirmed/payed yet
    int32 confirmation_pending = 2;
    // Number of people on waitlist
    int32 waitlist = 3;
    // Number of people on graylist
    int32 graylist = 4;
    // Number of attended participants
    int32 attended = 5;
  }

  // The number of participations in each state.
  ParticipationCounts participation_counts = 32;

  repeated eventmanager.frontend.email.Mail mails = 35;

  accounting.ProjectAccount account = 36;

  // NEXT ID = 38
}

message Whitelist {
  string whitelist_id = 1;
  eventmanager.frontend.person.Person person = 2;
}

// Possible groups allowed for event
enum AllowedParticipants {
  // All members of VIS can participate in this event
  VIS_MEMBERS = 0;
  // Any ETH member can participate in this event
  ALL_ETH = 1;
  // Only VIS active members can participate
  VIS_ACTIVE = 2;
  // Only whitelisted people can participate
  WHITELIST = 3;
  // Allow everyone even non-ETH members
  ANYBODY = 4;
}

// Possible Event stati
enum EventStati {
  // Event was modified since the last review (or is new)
  UNREVIEWED = 0;
  // Event has been reviewed and accepted
  REVIEWED = 1;
  // Event has gone public
  PUBLISHED = 2;
  // Registration is open and waitlist not filled yet
  REGISTRATION_OPEN = 3;
  // Event is full but people can still enter waiting list
  WAITING_LIST = 4;
  // Event registration is closed, waitlist is empty, graylisted users
  // can be accepted for the event
  GRAYLIST = 5;
  // Registration is closed, event hasn't started yet
  REGISTRATION_CLOSED = 6;
  // Event is currently happening
  ONGOING = 7;
  // Event has passed (and potentially open for feedback)
  PASSED = 8;
  // Event has been archived, no further changes
  ARCHIVED = 9;
  // Event has passed and feedback period has started
  FEEDBACK = 10;
}

// Sections of an event which can be reviewed separately
enum ReviewSections {
  // Date of the event (start and end time)
  EVENT_DATE = 0;
  // German title and description
  TEXT_DE = 1;
  // English title and description
  TEXT_EN = 2;
}

/**
 * For events with a participation fee (ConfirmationType value of PAYMENT) each
 * choice of options can result in a different price. A price is identified by
 * the exact (price relevant) choices made.
 */
message ParticipationCost {
  message Choice {
    string option_id = 1;
    oneof choice {
      bool checkbox_choice = 2;
      string dropdown_choice = 3;
    }
  }
  repeated Choice choices = 1;
  int32 cost_cent_non_vis = 2;
  int32 cost_cent_vis = 3;
}

frontend/feedback/feedback.proto

syntax = "proto3";

package eventmanager.frontend.feedback;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/feedback";

import "eventmanager/frontend/helper/helper.proto";
import "eventmanager/frontend/option/option.proto";
import "eventmanager/frontend/participant/participant.proto";
import "eventmanager/frontend/person/person.proto";

// The feedback service allows for managing all event feedbacks provided by
// event-participants.
service FeedbackService {
  // Gets single feedback instance
  rpc GetFeedback(GetFeedbackRequest) returns(Feedback) {}
  // Creates a feedback for a user and an event
  rpc CreateFeedback(CreateFeedbackRequest) returns(Feedback) {}
  // Updates the content of a feedback
  rpc UpdateFeedback(UpdateFeedbackRequest) returns(Feedback) {}
  // Gets a list of feedbacks
  rpc ListFeedbacks(ListFeedbacksRequest) returns(ListFeedbacksResponse) {}
  // Marks a given feedback as being offensive
  rpc MarkFeedbackOffensive(MarkFeedbackOffensiveRequest) returns(Feedback) {}

  // Gets single feedback template instance given its ID or the event's ID
  rpc GetFeedbackTemplate(GetFeedbackTemplateRequest)
      returns(FeedbackTemplate) {}
  // Creates a feedback template
  rpc CreateFeedbackTemplate(CreateFeedbackTemplateRequest)
      returns(FeedbackTemplate) {}
  // Updates a feedback template
  rpc UpdateFeedbackTemplate(UpdateFeedbackTemplateRequest)
      returns(FeedbackTemplate) {}
  // Gets a list of feedback templates
  rpc ListFeedbackTemplates(ListFeedbackTemplatesRequest)
      returns(ListFeedbackTemplatesResponse) {}
  // Resets a template back to default version
  rpc ResetFeedbackTemplateToDefault(ResetFeedbackTemplateToDefaultRequest)
      returns(FeedbackTemplate) {}
}

message GetFeedbackRequest { string feedback_id = 1; }

message CreateFeedbackRequest { Feedback feedback = 1; }

message UpdateFeedbackRequest { Feedback feedback = 1; }

message ListFeedbacksRequest {
  int32 page_size = 1;
  string page_token = 2;

  // Filters
  string event_id = 3;
  eventmanager.frontend.person.Person person = 4;
}

message ListFeedbacksResponse {
  repeated Feedback feedback = 1;
  string next_page_token = 2;
}

message MarkFeedbackOffensiveRequest { string feedback_id = 1; }

message GetFeedbackTemplateRequest {
  oneof id {
    string feedback_template_id = 1;
    string event_id = 2;
    bool default = 3;
  }
}

message CreateFeedbackTemplateRequest { FeedbackTemplate feedback = 1; }

message UpdateFeedbackTemplateRequest {
  FeedbackTemplate feedback = 1;
  bool default = 2; // true iff updating the admin default template is requested
}

message ListFeedbackTemplatesRequest {
  int32 page_size = 1;
  string page_token = 2;
}

message ListFeedbackTemplatesResponse {
  repeated FeedbackTemplate feedback = 1;
  string next_page_token = 2;
}

message ResetFeedbackTemplateToDefaultRequest { string id = 1; }

// A feedback for events. Each participant is allowed to give feedback.
message Feedback {
  string id = 1;

  // Fields in the feedback form
  repeated eventmanager.frontend.option.OptionChoice choices = 2;

  oneof origin {
    // Participation for which the feedback is given
    eventmanager.frontend.participant.Participation participation = 3;

    // if no participation is given, then the feedback is given by a helper
    eventmanager.frontend.helper.Helper helper = 4;

    // Feedback also needs to be possible by token only
    string helper_token = 6;
    string participation_token = 7;
  }

  string feedback_template_id = 5;

  // Offensive feedback is hidden
  bool offensive = 8;
}

message FeedbackTemplate {
  string id = 1;

  string event_id = 2;

  repeated eventmanager.frontend.option.Option questions = 3;

  bool disabled = 4;
}

frontend/file/file.proto

syntax = "proto3";

package eventmanager.frontend.file;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/file";

import "google/protobuf/empty.proto";

service FileService {
  // CreateFile stores a file (any file type is requested, max file size limited
  // by max grpc message size)
  rpc CreateFile(CreateFileRequest) returns(File) {}
  // GetFile returns a publicly accessible Url to the file
  rpc GetFile(GetFileRequest) returns(File) {}
  // DeleteFile deletes a file and removes all references to it from
  // participations and helpers
  rpc DeleteFile(DeleteFileRequest) returns(google.protobuf.Empty) {}
}

message CreateFileRequest {
  bytes data = 1;
  string name = 2;
  string event = 3;
}

message File {
  string id = 1;
  string url = 2;
  string event_id = 3;
}

message GetFileRequest { string id = 1; }

message DeleteFileRequest { string id = 1; }

frontend/graylist/graylist.proto

syntax = "proto3";

package eventmanager.frontend.graylist;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/graylist";

import "eventmanager/frontend/person/person.proto";
import "eventmanager/frontend/participant/participant.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";

// Managing graylisted people
service GraylistService {
  // Gets single graylist entry
  rpc GetGraylistEntry(GetGraylistEntryRequest) returns(GraylistEntry) {}
  // Adds a graylist entry
  rpc CreateGraylistEntry(CreateGraylistEntryRequest) returns(GraylistEntry) {}
  // Removes a graylist entry
  rpc DeleteGraylistEntry(DeleteGraylistEntryRequest)
      returns(google.protobuf.Empty) {}
  // Updates a graylist entry
  rpc UpdateGraylistEntry(UpdateGraylistEntryRequest) returns(GraylistEntry) {}
  // Gets a list of graylist entries
  rpc ListGraylists(ListGraylistsRequest) returns(GraylistEntryList) {}
  // Gets graylist entry for user
  rpc GetGraylistEntryForUser(GetGraylistEntryForUserRequest)
      returns(GraylistEntry) {}
}

message GetGraylistEntryRequest { string entry_id = 1; }

message CreateGraylistEntryRequest {
  GraylistEntry graylist_entry = 1;
  eventmanager.frontend.participant.Participation participation = 2;
}

message DeleteGraylistEntryRequest { string entry_id = 1; }

message UpdateGraylistEntryRequest { GraylistEntry graylist_entry = 1; }

message ListGraylistsRequest {
  int32 page_size = 1;
  string page_token = 2;
}

message GraylistEntry {
  string id = 1;
  // Id of the user in the graylist
  eventmanager.frontend.person.Person person = 2;
  // Reason behind the graylisting
  string reason = 3;
  // every graylistEntry has a finite duration
  google.protobuf.Timestamp graylisted_until = 4;
}

message GraylistEntryList {
  repeated GraylistEntry graylist_entry = 1;
  string next_page_token = 2;
}

message GetGraylistEntryForUserRequest {
  eventmanager.frontend.person.Person person = 1;
}

frontend/helper/helper.proto

syntax = "proto3";

package eventmanager.frontend.helper;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/helper";

import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";

import "eventmanager/frontend/option/option.proto";
import "eventmanager/frontend/participant/participant.proto";
import "eventmanager/frontend/person/person.proto";

// HelperService is used to manage helpers for events:
// - Students can declare their intent to be a helper
// - Students can search for open helper slots
// - Students can sign up for helper slots,
// - Event organisers can list helpers of their events
// - Event organisers can reach out to their helpers
service HelperService {
  // Gets the helper's intent of the provided user
  rpc GetHelpersIntent(GetHelpersIntentRequest) returns(HelpersIntent) {}
  // Updates the helper's intent of the provided user
  rpc UpdateHelpersIntent(UpdateHelpersIntentRequest) returns(HelpersIntent) {}

  // Creates the helperslot
  rpc CreateHelperSlot(CreateHelperSlotRequest) returns(HelperSlot) {}
  // Gets the helperslot
  rpc GetHelperSlot(GetHelperSlotRequest) returns(HelperSlot) {}
  // Deletes the helperslot and all of the signed up helpers
  rpc DeleteHelperSlot(DeleteHelperSlotRequest) returns(google.protobuf.Empty) {
  }
  // Updates the helperslot
  rpc UpdateHelperSlot(UpdateHelperSlotRequest) returns(HelperSlot) {}
  // Lists open helper slots for the current user
  rpc ListOpenHelperSlots(ListOpenHelperSlotsRequest)
      returns(ListOpenHelperSlotsResponse) {}
  // Lists helper slots the provided user has "booked"
  rpc ListBookedHelperSlots(ListBookedHelperSlotsRequest)
      returns(ListBookedHelperSlotsResponse) {}
  // Lists helper slots for provided event
  rpc ListHelperSlotsForEvent(ListHelperSlotsForEventRequest)
      returns(ListHelperSlotsForEventResponse) {}

  // Books a helper slot for the provided user.
  rpc CreateHelper(CreateHelperRequest) returns(Helper) {}
  // Retuns the helper slot booking.
  rpc GetHelper(GetHelperRequest) returns(Helper) {}
  // Updates the helper slot booking.
  rpc UpdateHelper(UpdateHelperRequest) returns(Helper) {}
  // Deletes the helper slot booking.
  rpc DeleteHelper(DeleteHelperRequest) returns(google.protobuf.Empty) {}
  // Cancels the signup for the helper slot
  rpc CancelHelper(CancelHelperRequest) returns(Helper) {}
  // Resets the helper signup to SIGNED_UP if possible
  rpc ResetHelperStatus(ResetHelperStatusRequest) returns(Helper) {}
  // Lists helpers for given filter.
  rpc ListHelpers(ListHelpersRequest) returns(ListHelpersResponse) {}

  // Sends out a message to all helpers of the event. Helpers joining after
  // the message is sent out will receive the message once they join.
  rpc CreateHelperMessage(CreateHelperMessageRequest) returns(HelperMessage) {}
}

message HelperSlot {
  string id = 1;
  string event_id = 2;
  // Needed to access event from public UI
  string event_token = 14;
  string role_de = 3;        // Arbitrary text (e.g. "aufräumen")
  string role_en = 4;        // Arbitrary text (e.g. "cleanup")
  string description_de = 5; // Arbitrary text describing the slot
  string description_en = 6;
  google.protobuf.Timestamp from_time = 7;
  google.protobuf.Timestamp until_time = 8;
  int32 slot_count = 9; // Maximum number of helpers for slot.
  bool vis_member_only = 10;
  HelperSlotStatusEnum status = 11;
  repeated eventmanager.frontend.option.Option options = 12;
  int32 signed_up = 13; // Number of helpers currently signed up for slot
  google.protobuf.Timestamp latest_cancel_time = 15;
  // NEXT_ID=16;
}

// Defines the status of a helperslot
enum HelperSlotStatusEnum {
  // the slot is open for appliances
  OPEN = 0;
  // the slot is closed for appliances
  CLOSED = 1;
}

// Defines the status of a helper
enum HelperStatusEnum {
  // the helper has signed up
  SIGNED_UP = 0;
  // the helper has withdrawn his sign-up
  CANCELLED = 1;
  // the helper was helping at the event
  HELPED = 2;
  // the helper signed up but did not turn up at the event
  SKIPPED = 3;
}

message HelpersIntent {
  bool willing = 1; // True if the user wants to be a helper
  eventmanager.frontend.participant.Language preferred_language = 2;
}

message Helper {
  string id = 1;
  HelperSlot slot = 2;
  eventmanager.frontend.person.Person person =
      3; // Generally, every ETH student can become a helper
  HelperStatusEnum status = 4;
  repeated eventmanager.frontend.option.OptionChoice choices = 5;
  eventmanager.frontend.participant.Language language = 6;
}

// The HelperMessage will be sent out to all helpers of the event.
message HelperMessage {
  string helper_slot_id = 1;
  string subject = 2;
  string message_de = 3;
  string message_en = 4;
  google.protobuf.Timestamp time_sent = 7;
}

message GetHelpersIntentRequest {
  eventmanager.frontend.person.Person person = 1; // Requested user
}

message UpdateHelpersIntentRequest {
  eventmanager.frontend.person.Person person = 1;
  HelpersIntent intent = 2;
}

message CreateHelperSlotRequest { HelperSlot slot = 1; }

message GetHelperSlotRequest { string slot_id = 1; }

message UpdateHelperSlotRequest { HelperSlot slot = 1; }

message DeleteHelperSlotRequest { string slot_id = 1; }

message UpdateHelperRequest { Helper helper = 1; }

message ListOpenHelperSlotsRequest {
  string event_id = 1; // Optional filter for the event
  string role = 2;     // Optional filter for the role
  eventmanager.frontend.person.Person person = 3; // Person that wants to know
  int32 page_size = 4;
  string page_token = 5;
}

message ListOpenHelperSlotsResponse {
  repeated HelperSlot slots = 1;
  string next_page_token = 2;
}

message ListBookedHelperSlotsRequest {
  eventmanager.frontend.person.Person person = 1;
  int32 page_size = 2;
  string page_token = 3;
  bool show_passed = 4; // Show past helper slots
}

message ListBookedHelperSlotsResponse {
  repeated HelperSlot slots = 1;
  string next_page_token = 2;
}

message ListHelperSlotsForEventRequest {
  string event_id = 1;
  int32 page_size = 2;
  string page_token = 3;
  eventmanager.frontend.person.Person person = 4; // Person who wants to know
}

message ListHelperSlotsForEventResponse {
  repeated HelperSlot slots = 1;
  string next_page_token = 2;
}

message ListHelpersRequest {
  repeated string event_ids = 1;

  // Which HelperSlots to filter for
  message HelperSlotFilter {
    // Search only for helpers in slots with given IDs
    repeated string slot_id = 1;
    // Text for substring search in role (english and german)
    string role = 2;
    // Text for substring search in description (english and german)
    string description = 3;

    enum MemberFilter {
      // user is vis member
      VIS = 0;
      // user is non vis member
      NON_VIS = 1;
    }
    MemberFilter member_filter = 4;

    // Search for slots with specific stati
    repeated HelperSlotStatusEnum stati = 5;

    SortBy sort_by = 6;

    enum SortBy {
      // Date created
      CREATED = 0;
      // ID
      ID = 1;
      // Role De
      ROLE_DE = 2;
      // Role En
      ROLE_EN = 3;
      // Description german (lexicographic)
      DESCRIPTION_DE = 4;
      // Description english (lexicographic)
      DESCRIPTION_EN = 5;
      // From Time
      FROM_TIME = 6;
      // Until Time
      UNTIL_TIME = 7;
    }

    // find slots that occurred from 'from' onwards
    google.protobuf.Timestamp from = 7;

    // find slots that occurred before 'until'
    google.protobuf.Timestamp until = 8;
  }
  HelperSlotFilter filter = 2;
  int32 page_size = 3;
  string page_token = 4;

  repeated eventmanager.frontend.option.ChoiceFilter choice_filter = 5;
  repeated person.Person persons = 6;
}

message ListHelpersResponse {
  repeated Helper helpers = 1;
  string next_page_token = 2;
}

message CreateHelperRequest { Helper helper = 1; }

message CancelHelperRequest { string helper_id = 1; }

message ResetHelperStatusRequest { string helper_id = 1; }

message GetHelperRequest { string helper_id = 1; }

message DeleteHelperRequest { string helper_id = 1; }

message CreateHelperMessageRequest { HelperMessage helper_message = 1; }

frontend/log/log.proto

syntax = "proto3";

package eventmanager.frontent.log;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/log";

import "eventmanager/frontend/person/person.proto";
import "google/protobuf/timestamp.proto";

// Managing logs
service LogService {
  // Gets single log entry
  rpc GetLog(GetLogRequest) returns(Log) {}
  // Gets a list of log entries
  rpc ListLogs(ListLogsRequest) returns(LogList) {}
}

message GetLogRequest { string log_id = 1; }

message ListLogsRequest {
  // which logs to return
  message LogFilter {
    // id of the event
    repeated string event_ids = 1;
    // email of person
    repeated eventmanager.frontend.person.Person persons = 2;
    // type of the log entry
    repeated ActionType types = 3;
    // text for substring search in info text
    string info = 4;
    // Selecting log entries generated from automatic actions
    enum System { SYSTEM_AND_PEOPLE = 0; SYSTEM_ONLY = 1; PEOPLE_ONLY = 2; }
    System system_logs = 5;
  }
  LogFilter filter = 1;
  int32 page_size = 2;
  string page_token = 3;
}

// representation of a single log entry
message Log {
  string id = 1;
  google.protobuf.Timestamp created_at = 2;
  string person_email = 3;
  string event_id = 4;
  ActionType action_type = 5;
  string info = 6;
}

message LogList {
  repeated Log log = 1;
  string next_page_token = 2;
}

// all log action types
enum ActionType {
  EVENT_CREATED = 0; EVENT_DELETED = 1; EVENT_UPDATED = 2; EVENT_CLONED = 3;
  EVENT_ACCEPT_SECTIONS_CHANGE = 4;
  EVENT_REJECTED_CHANGE = 5;
  EVENT_METADATA_UPDATE = 6;
  GRAYLIST_ENTRY_CREATED = 7;
  GRAYLIST_ENTRY_DELETED = 8;
  GRAYLIST_ENTRY_UPDATED = 9;
  EMAIL_SENT = 10;
  HELPER_INTENT_UPDATE = 11;
  HELPER_SLOT_CREATE = 12;
  HELPER_SLOT_UPDATE = 13;
  HELPER_CREATE = 14;
  HELPER_UPDATE = 15;
  HELPER_DELETE = 16;
  HELPER_MESSAGE_CREATE = 17;
  EMAIL_CREATE = 18;
  EMAIL_UPDATE = 19;
  EMAIL_DELETE = 20;
  EMAIL_REVIEWABLE = 21;
  EMAIL_REJECTED_CHANGE = 22;
  EMAIL_ACCEPTED_CHANGE = 23;
  STATUS_EMAIL_DEFAULT_CHANGE = 24;
  FEEDBACK_UPDATED = 25;
  FEEDBACK_TEMPLATE_UPDATED = 26;
  PARTICIPATION_UPDATED = 27;
  EMAIL_SEND_FAILED = 28;
  ERROR_UPDATE_ALL_EVENT_DATUM_STATI = 29;
  ERROR_UPDATE_ALL_PARTICIPATION_CONFIRMATION_STATI = 30;
  ERROR_SEND_STATUS_MAIL_FROM_MAIL_LIST = 31;
  ERROR_SEND_TIMED_MAILS = 32;
  ERROR_LIST_MAILS = 33;
  EMAIL_DEFAULT_DELETED = 34;
  ERROR_AUTOFILL_FROM_WAITLIST_AND_GRAYLIST = 35;
  FEEDBACK_MARKED_OFFENSIVE = 36;
  HELPER_CANCELLED = 37;
  PARTICIPATION_CREATED = 38;
  PARTICIPATION_CANCELLED = 39;
  PARTICIPATION_RE_SIGNUP = 40;
  HELPER_RE_SIGNUP = 41;
  FEEDBACK_SUBMITTED = 42;
  //    TODO add more enums
}

frontend/option/option.proto

syntax = "proto3";

package eventmanager.frontend.option;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/option";

import "google/type/date.proto";
import "google/protobuf/timestamp.proto";

/**
 * Events/Helper slots/Feedback questions for the participants to configure
 * (such as being vegetarian or not). There are multiple different types of
 * options. Each option has an associated label which describes what's being
 * configured.
 */
message Option {
  string id = 1;
  string label_de = 2;
  string label_en = 3;
  oneof options {
    CheckboxOption check = 4;
    FreeTextOption text = 5;
    FileOption file = 6;
    NumberOption number = 7;
    DropdownOption dropdown = 8;
    StarOption stars = 9;
    DateOption date = 10;
    DatetimeOption datetime = 11;
  }
  bool required = 12;
  // Set only for event options, is the id of the draft version of the option
  string edit_id = 13;
}

// Checkbox options (true/false)
message CheckboxOption { bool price_relevant = 1; }

// Free text options
message FreeTextOption {}

// Option requiring a file
message FileOption {}

// Any input requiring just a number
message NumberOption {}

// Dropdown option with multiple options to chose from
message DropdownOption {
  // The dropdown choices
  message Choice {
    string id = 1;
    string label_de = 2;
    string label_en = 3;
    // Set only for event dropdown options, is the id of the draft version
    string edit_id = 4;
  }
  repeated Choice choices = 1;
  bool price_relevant = 2;
}

// Rating something (star value between 1 and 5)
message StarOption {}

// Date option
message DateOption {}

// Datetime option
message DatetimeOption {}

// Options can come in many forms, each option is of exactly one type
message OptionChoice {
  string id = 1;        // The identifier of the choice
  string option_id = 2; // The Identifier of the option
  oneof option {
    // Represents a file ID
    string file_option = 3;

    // Contains text
    string text_option = 4;

    // Answer to a number option
    int32 number_option = 5;

    // Star value (1-5)
    int32 star_option = 6;

    // Represents the ID of a dropdown-option
    string dropdown_option = 7;

    // Contains the response to a checkbox type question
    bool checkbox_option = 8;

    // Date options
    google.type.Date date_option = 9;

    // Time options
    google.protobuf.Timestamp time_option = 10;

    // User did not give answer
    bool no_answer = 11;
  }
}

// used to filter for specific participation answers
message ChoiceFilter {
  string option_id = 1; // The Identifier of the option on which we filter
  // Textsnippet to match (case-insensitive substring match)
  string text_filter = 2;

  message NRange {
    int32 range_min = 1;
    int32 range_max = 2;
  }
  // Number range to filter
  oneof number_filter {
    NRange number_range = 3;
    // set min of range, but leave max open
    int32 number_range_min_with_top_open = 4;
    // set max of range, but leave min open
    int32 number_range_max_with_bottom_open = 5;
  }

  // Filter for dropdownoptions
  repeated string dropdown_filters = 6;

  // Filter for checkboxes
  bool checkbox_filter = 7;

  message DRange {
    google.type.Date range_min = 1;
    google.type.Date range_max = 2;
  }
  // Date range
  oneof date_filter {
    DRange date_range = 8;
    // set min of range, but leave max open
    google.type.Date date_range_min_with_top_open = 9;
    // set max of range, but leave min open
    google.type.Date date_range_max_with_bottom_open = 10;
  }

  message TRange {
    google.protobuf.Timestamp range_min = 1;
    google.protobuf.Timestamp range_max = 2;
  }
  // Time range
  oneof time_filter {
    TRange time_range = 11;
    // set min of range, but leave max open
    google.protobuf.Timestamp time_range_min_with_top_open = 12;
    // set max of range, but leave min open
    google.protobuf.Timestamp time_range_max_with_bottom_open = 13;
  }
}

frontend/participant/participant.proto

syntax = "proto3";

package eventmanager.frontend.participant;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/participant";

import "google/protobuf/empty.proto";
import "eventmanager/frontend/option/option.proto";
import "eventmanager/frontend/person/person.proto";
import "eventmanager/frontend/event/event.proto";
import "google/protobuf/timestamp.proto";

// ParticipationService allows managing event participation.
service ParticipationService {
  // Creates a participation. State changes (like moving from
  // waitlist to pending confirmation) are automatically triggered
  // where required and possible.
  rpc CreateParticipation(CreateParticipationRequest) returns(Participation) {}

  // Updates a registration. As in 'CreateParticipation',
  // state changes are automatically triggered in case they are possible.
  rpc UpdateParticipation(UpdateParticipationRequest) returns(Participation) {}

  // Puts a participation in CANCELLED state back to its proper place (in
  // waitlist, etc.)
  rpc ResetParticipationStatus(ResetParticipationStatusRequest)
      returns(Participation) {}

  // Get participation info
  rpc GetParticipation(GetParticipationRequest) returns(Participation) {}

  // Checks if the participation can be created (no conflicts with other events,
  // user allowed)
  rpc CanCreateParticipation(CanCreateParticipationRequest)
      returns(CanCreateParticipationResponse) {}

  // Deletes a participation
  rpc DeleteParticipation(DeleteParticipationRequest)
      returns(google.protobuf.Empty) {}

  // Lists all participants for an event or person
  rpc ListParticipations(ListParticipationsRequest)
      returns(ListParticipationsResponse) {}
}

message CreateParticipationRequest { Participation participation = 1; }

message UpdateParticipationRequest { Participation participation = 1; }

message ResetParticipationStatusRequest { string id = 1; }

message GetParticipationRequest { string id = 1; }

message CanCreateParticipationRequest { Participation participation = 1; }

message CanCreateParticipationResponse {
  repeated eventmanager.frontend.event.Event conflicting_events = 1;
}

message DeleteParticipationRequest { string id = 1; }

message ListParticipationsRequest {

  message ParticipationFilter {
    repeated string event_ids = 1;
    repeated eventmanager.frontend.person.Person persons = 2;
    repeated ParticipationStatus stati = 3;
    repeated eventmanager.frontend.option.ChoiceFilter choice_filter = 4;
    repeated Language language = 5;
  }

  ParticipationFilter filter = 1;
  int32 page_size = 2;
  string page_token = 3;
}

message ListParticipationsResponse {
  repeated Participation participations = 1;
  string next_page_token = 2;
}

enum Language { EN = 0; DE = 1; }

// Participation represents the state of a participant for an event.
message Participation {
  string id = 1;
  string event_id = 2;
  // Needed to access event from public UI
  string event_token = 8;
  person.Person person = 3;
  repeated eventmanager.frontend.option.OptionChoice options = 4;
  int32 cost_cent = 5;

  Language language = 6;

  ParticipationStatus status = 7;

  // The latest time the user can confirm their participation before being
  // auto-kicked
  google.protobuf.Timestamp confirm_by = 9;

  // NEXT_ID=10;
}

// Possible participation stati
enum ParticipationStatus {
  // Default state, user is in waitlist
  WAITLIST = 0;
  // User passed waitlist/graylist status and needs to confirm
  PENDING_CONFIRMATION = 1;
  // User is confirmed and the event not over yet
  CONFIRMED = 2;
  // User has deregistered/got deregistered
  DEREGISTERED = 3;
  // Event is over and user can enter feedback
  FEEDBACK = 4;
  // User is in graylist and thus not eligible for event (yet)
  GRAYLIST = 5;
  // User attended event (needs to be confirmed by organiser)
  ATTENDED = 6;
  // User missed the event despite being confirmed
  MISSED_EVENT = 7;
  // Feedback phase is over, participation can't be changed anymore
  ARCHIVED = 8;
  // User missed confirmation and thus can't participate in event
  MISSED_CONFIRMATION = 9;
}

frontend/person/person.proto

syntax = "proto3";

package eventmanager.frontend.person;
option go_package = "gitlab.ethz.ch/vis/cat/eventmanager/frontend/person";

import "google/protobuf/empty.proto";

service PersonService {
  // Returns the currently logged in user or an error if not logged in
  rpc GetLoggedinPerson(google.protobuf.Empty) returns(Person) {}

  // Returns true iff the person logged in is a board member
  rpc GetPrivileged(google.protobuf.Empty) returns(PrivilegedResponse) {}

  // Lists persons matching filter
  rpc ListPersons(ListPersonsRequest) returns(ListPersonsResponse) {}
}

// Proto containing a person's name and email
message Person {
  string first_name = 1;
  string last_name = 2;
  oneof kind {
    string nethz = 3;
    string external_email = 4;
    string vis_username = 5;
  }
}

// Proto containing flag if user is Board member
message PrivilegedResponse { bool privileged = 1; }

message ListPersonsRequest {
  // String to substring match all persons
  string substring = 1;
  int32 pagesize = 2;
  string token = 3;
}

message ListPersonsResponse {
  repeated Person persons = 1;
  string next_page_token = 2;
}