sidequest_common.models.events ============================== .. py:module:: sidequest_common.models.events .. autoapi-nested-parse:: Guild events and member signups. A raid is one kind of guild event; see ``EventKind``. ``EventSeries`` is a repeating-event rule (e.g. "every Friday at 7pm"); it never shows up to members directly. Instead the API materializes it into ordinary ``GuildEvent`` rows (its "occurrences") up to a 14-day horizon, each with its own sign-ups and Discord event. See ``local://event-series-contract.md``. Attributes ---------- .. autoapisummary:: sidequest_common.models.events.EVENT_KINDS sidequest_common.models.events.EVENT_STATUSES sidequest_common.models.events.ROLE_KINDS sidequest_common.models.events.SIGNUP_ACTIVITY_ACTIONS sidequest_common.models.events.SIGNUP_ROLES sidequest_common.models.events.SIGNUP_STATUSES sidequest_common.models.events.WOW_CLASSES Classes ------- .. autoapisummary:: sidequest_common.models.events.DiscordEventTombstone sidequest_common.models.events.EventDiscordInterest sidequest_common.models.events.EventSeries sidequest_common.models.events.EventSignup sidequest_common.models.events.EventSignupActivity sidequest_common.models.events.EventVoicePresence sidequest_common.models.events.GuildEvent Module Contents --------------- .. py:class:: DiscordEventTombstone Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` A Discord scheduled event whose guild event was deleted on the site; the bot deletes it, then this row. .. py:attribute:: discord_event_id :type: sqlalchemy.orm.Mapped[int] .. py:class:: EventDiscordInterest Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One Discord user who clicked "Interested" on a guild event's linked Discord scheduled event. Discord -> site only: the API can't mark a Discord user interested on the site's behalf. Replaced wholesale per event whenever the bot reports the current interested-user list. .. py:attribute:: discord_id :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: event_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:class:: EventSeries Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` A repeating guild event rule ("every Friday at 7pm"). Materializes into ``GuildEvent`` occurrences. ``weekdays`` is ``0`` (Monday) through ``6`` (Sunday). ``interval_weeks`` is ``1`` (every week) or ``2`` (every other week, anchored on ``starts_on``'s week). ``start_time`` is wall-clock time in ``timezone`` (IANA, e.g. ``"America/Chicago"``); occurrences convert it to UTC per-occurrence via ``zoneinfo``, so DST shifts land correctly. ``skipped_dates`` (ISO date strings) are occurrence dates a member deleted; they are never recreated. ``active`` is cleared once ``until`` has passed. ``ep_override`` is copied onto occurrences. .. py:attribute:: active :type: sqlalchemy.orm.Mapped[bool] .. py:attribute:: duration_minutes :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: ep_override :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: interval_weeks :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: kind :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: location :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: notes :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: occurrences :type: sqlalchemy.orm.Mapped[list[GuildEvent]] .. py:attribute:: size :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: skipped_dates :type: sqlalchemy.orm.Mapped[list[str]] .. py:attribute:: start_time :type: sqlalchemy.orm.Mapped[datetime.time] .. py:attribute:: starts_on :type: sqlalchemy.orm.Mapped[datetime.date] .. py:attribute:: timezone :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: title :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: until :type: sqlalchemy.orm.Mapped[datetime.date | None] .. py:attribute:: weekdays :type: sqlalchemy.orm.Mapped[list[int]] .. py:class:: EventSignup Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One member's signup for one guild event. ``class`` is a Python keyword, so the column is ``wow_class``. ``character``/``wow_class``/``role`` are nullable: only role kinds (raid, dungeon) require them. .. py:attribute:: attended :type: sqlalchemy.orm.Mapped[bool | None] .. py:attribute:: bench :type: sqlalchemy.orm.Mapped[bool] .. py:attribute:: character :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: chart_group :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: comment :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: event_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: is_walk_in :type: sqlalchemy.orm.Mapped[bool] .. py:attribute:: member_id :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: role :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: status :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: wow_class :type: sqlalchemy.orm.Mapped[str | None] .. py:class:: EventSignupActivity Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One sign-up status or comment change, queued to post as a comment on the event's Discord forum thread. ``action`` is one of ``SIGNUP_ACTIVITY_ACTIONS``: ``going``/``maybe``/``not_going`` mirror a signup's ``status``, and ``withdrew`` marks a deleted signup. ``detail`` is the short "Character · Class · Role" line for role-kind events, else ``None``. ``discord_id`` lets the bot @-mention the member (rows written before it existed have none), and ``comment`` is the sign-up's comment when the row was written. ``posted_at`` is set once the bot acks it; rows for events with no Discord thread stay unposted (and are dropped by the API if older than 24h) rather than posted. .. py:attribute:: action :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: comment :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: detail :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: discord_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: display_name :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: event_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: posted_at :type: sqlalchemy.orm.Mapped[datetime.datetime | None] .. py:class:: EventVoicePresence Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` Minutes a Discord member spent in the guild's voice channels while one event ran. Bot-reported (``PUT /api/guilds/{guild_id}/attendance/voice``) with cumulative minutes-so-far per member for the event; each push stores ``max(existing, reported)``, so repeated pushes -- including after a bot restart resets its in-memory counter -- are idempotent and never lower an already-accumulated total. .. py:attribute:: discord_id :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: event_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: minutes :type: sqlalchemy.orm.Mapped[int] .. py:class:: GuildEvent Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One scheduled guild event, mirrored to a Discord scheduled event when the bot is running. ``kind`` distinguishes raids from other events (dungeon runs, leveling sessions, PvP, social hangs); only "role kinds" (raid, dungeon) use the character/class/role sign-up fields and the raid chart. ``location`` is ``""`` and ``size`` is ``None`` for events first created in Discord (Discord has neither). The ``discord_*`` columns belong to the two-way sync: ``discord_pushed_hash`` is the hash of the last desired state the bot wrote to Discord, ``discord_seen_hash`` the hash of the last Discord state the bot reported. An event needs a push when its current desired-state hash differs from ``discord_pushed_hash``; a Discord report whose hash equals ``discord_seen_hash`` is our own echo and is ignored. ``series_id``/``occurrence_date`` link an occurrence back to the ``EventSeries`` that materialized it; ``series_detached`` is set once an officer edits or deletes that one occurrence, so series-wide edits stop touching it. ``ep_override`` is the officer-set attendance EP; ``None`` means the kind's preset (``EVENT_KIND_EP``), so retuning a preset reaches every event that kept it. .. py:attribute:: attendance_taken_at :type: sqlalchemy.orm.Mapped[datetime.datetime | None] .. py:attribute:: discord_event_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: discord_pushed_hash :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: discord_seen_hash :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: discord_thread_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: ends_at :type: sqlalchemy.orm.Mapped[datetime.datetime | None] .. py:attribute:: ep_override :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: kind :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: location :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: notes :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: occurrence_date :type: sqlalchemy.orm.Mapped[datetime.date | None] .. py:attribute:: series :type: sqlalchemy.orm.Mapped[EventSeries | None] .. py:attribute:: series_detached :type: sqlalchemy.orm.Mapped[bool] .. py:attribute:: series_id :type: sqlalchemy.orm.Mapped[uuid.UUID | None] .. py:attribute:: size :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: starts_at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: status :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: title :type: sqlalchemy.orm.Mapped[str] .. py:data:: EVENT_KINDS :value: ('raid', 'dungeon', 'leveling', 'pvp', 'social', 'transmog', 'other') .. py:data:: EVENT_STATUSES :value: ('scheduled', 'cancelled', 'completed') .. py:data:: ROLE_KINDS :value: ('raid', 'dungeon') .. py:data:: SIGNUP_ACTIVITY_ACTIONS :value: ('going', 'maybe', 'not_going', 'withdrew') .. py:data:: SIGNUP_ROLES :value: ('tank', 'healer', 'dps') .. py:data:: SIGNUP_STATUSES :value: ('signed', 'tentative', 'absent') .. py:data:: WOW_CLASSES :value: ('warrior', 'paladin', 'hunter', 'rogue', 'priest', 'shaman', 'mage', 'warlock', 'druid')