sidequest_common.models.events#

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#

Classes#

DiscordEventTombstone

A Discord scheduled event whose guild event was deleted on the site; the bot deletes it, then this row.

EventDiscordInterest

One Discord user who clicked "Interested" on a guild event's linked Discord scheduled event.

EventSeries

A repeating guild event rule ("every Friday at 7pm"). Materializes into GuildEvent occurrences.

EventSignup

One member's signup for one guild event. class is a Python keyword, so the column is wow_class.

EventSignupActivity

One sign-up status or comment change, queued to post as a comment on the event's Discord forum thread.

EventVoicePresence

Minutes a Discord member spent in the guild's voice channels while one event ran.

GuildEvent

One scheduled guild event, mirrored to a Discord scheduled event when the bot is running.

Module Contents#

class sidequest_common.models.events.DiscordEventTombstone#

Bases: advanced_alchemy.base.UUIDAuditBase

A Discord scheduled event whose guild event was deleted on the site; the bot deletes it, then this row.

discord_event_id: sqlalchemy.orm.Mapped[int]#
class sidequest_common.models.events.EventDiscordInterest#

Bases: 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.

discord_id: sqlalchemy.orm.Mapped[int]#
event_id: sqlalchemy.orm.Mapped[uuid.UUID]#
class sidequest_common.models.events.EventSeries#

Bases: 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.

active: sqlalchemy.orm.Mapped[bool]#
duration_minutes: sqlalchemy.orm.Mapped[int | None]#
ep_override: sqlalchemy.orm.Mapped[int | None]#
interval_weeks: sqlalchemy.orm.Mapped[int]#
kind: sqlalchemy.orm.Mapped[str]#
location: sqlalchemy.orm.Mapped[str]#
notes: sqlalchemy.orm.Mapped[str | None]#
occurrences: sqlalchemy.orm.Mapped[list[GuildEvent]]#
size: sqlalchemy.orm.Mapped[int | None]#
skipped_dates: sqlalchemy.orm.Mapped[list[str]]#
start_time: sqlalchemy.orm.Mapped[datetime.time]#
starts_on: sqlalchemy.orm.Mapped[datetime.date]#
timezone: sqlalchemy.orm.Mapped[str]#
title: sqlalchemy.orm.Mapped[str]#
until: sqlalchemy.orm.Mapped[datetime.date | None]#
weekdays: sqlalchemy.orm.Mapped[list[int]]#
class sidequest_common.models.events.EventSignup#

Bases: 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.

attended: sqlalchemy.orm.Mapped[bool | None]#
bench: sqlalchemy.orm.Mapped[bool]#
character: sqlalchemy.orm.Mapped[str | None]#
chart_group: sqlalchemy.orm.Mapped[int | None]#
comment: sqlalchemy.orm.Mapped[str | None]#
event_id: sqlalchemy.orm.Mapped[uuid.UUID]#
is_walk_in: sqlalchemy.orm.Mapped[bool]#
member_id: sqlalchemy.orm.Mapped[int]#
role: sqlalchemy.orm.Mapped[str | None]#
status: sqlalchemy.orm.Mapped[str]#
wow_class: sqlalchemy.orm.Mapped[str | None]#
class sidequest_common.models.events.EventSignupActivity#

Bases: 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.

action: sqlalchemy.orm.Mapped[str]#
comment: sqlalchemy.orm.Mapped[str | None]#
detail: sqlalchemy.orm.Mapped[str | None]#
discord_id: sqlalchemy.orm.Mapped[int | None]#
display_name: sqlalchemy.orm.Mapped[str]#
event_id: sqlalchemy.orm.Mapped[uuid.UUID]#
posted_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
class sidequest_common.models.events.EventVoicePresence#

Bases: 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.

discord_id: sqlalchemy.orm.Mapped[int]#
event_id: sqlalchemy.orm.Mapped[uuid.UUID]#
minutes: sqlalchemy.orm.Mapped[int]#
class sidequest_common.models.events.GuildEvent#

Bases: 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.

attendance_taken_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
discord_event_id: sqlalchemy.orm.Mapped[int | None]#
discord_pushed_hash: sqlalchemy.orm.Mapped[str | None]#
discord_seen_hash: sqlalchemy.orm.Mapped[str | None]#
discord_thread_id: sqlalchemy.orm.Mapped[int | None]#
ends_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
ep_override: sqlalchemy.orm.Mapped[int | None]#
kind: sqlalchemy.orm.Mapped[str]#
location: sqlalchemy.orm.Mapped[str]#
notes: sqlalchemy.orm.Mapped[str | None]#
occurrence_date: sqlalchemy.orm.Mapped[datetime.date | None]#
series: sqlalchemy.orm.Mapped[EventSeries | None]#
series_detached: sqlalchemy.orm.Mapped[bool]#
series_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
size: sqlalchemy.orm.Mapped[int | None]#
starts_at: sqlalchemy.orm.Mapped[datetime.datetime]#
status: sqlalchemy.orm.Mapped[str]#
title: sqlalchemy.orm.Mapped[str]#
sidequest_common.models.events.EVENT_KINDS = ('raid', 'dungeon', 'leveling', 'pvp', 'social', 'transmog', 'other')#
sidequest_common.models.events.EVENT_STATUSES = ('scheduled', 'cancelled', 'completed')#
sidequest_common.models.events.ROLE_KINDS = ('raid', 'dungeon')#
sidequest_common.models.events.SIGNUP_ACTIVITY_ACTIONS = ('going', 'maybe', 'not_going', 'withdrew')#
sidequest_common.models.events.SIGNUP_ROLES = ('tank', 'healer', 'dps')#
sidequest_common.models.events.SIGNUP_STATUSES = ('signed', 'tentative', 'absent')#
sidequest_common.models.events.WOW_CLASSES = ('warrior', 'paladin', 'hunter', 'rogue', 'priest', 'shaman', 'mage', 'warlock', 'druid')#