sidequest_common.models.epgp#
EPGP loot system: WoW characters, raid sessions/encounters, loot awards, and the EP/GP ledger.
Source of truth is the site (local://addon-contract.md); the sidequest-sync addon exports raid facts and
ledger entries as an SQX1: string, an officer pastes it (or uploads SavedVariables) into
POST /api/epgp/imports, and the import service upserts by each record’s addon-assigned external_id so
re-importing the same string is a no-op. GP/EP decay (15% per weekly reset) is never stored: it’s applied on read
from EpgpLedgerEntry.at (see sidequest_api.domain.epgp.services). A voided row (voided_at set) is kept
forever and excluded from standings; it can never be un-voided.
Classes#
One boss pull (or kill) within a raid session, for the loot log and the progression board. |
|
One |
|
One EP or GP change: an addon-exported award, a loot GP charge, or a site-made officer adjustment. |
|
A long-lived bearer token for the |
|
One item awarded during a raid session, with the GP charge recorded as a twin |
|
One raid night, as recorded by the addon's session recorder and exported by |
|
One character's attendance window within one raid session, standby (bench) or not. |
|
One in-game character, keyed |
Module Contents#
- class sidequest_common.models.epgp.Encounter#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne boss pull (or kill) within a raid session, for the loot log and the progression board.
characters/standbyare the addon-reported character keys present for the pull; kept as JSON rather than a join table since nothing queries them per-character (only the session’sSessionCharacterPresencerows drive attendance and standings).- characters: sqlalchemy.orm.Mapped[list[str]]#
- difficulty_id: sqlalchemy.orm.Mapped[int]#
- encounter_id: sqlalchemy.orm.Mapped[int]#
- ended_at: sqlalchemy.orm.Mapped[datetime.datetime]#
- external_id: sqlalchemy.orm.Mapped[str]#
- name: sqlalchemy.orm.Mapped[str]#
- session_id: sqlalchemy.orm.Mapped[uuid.UUID]#
- size: sqlalchemy.orm.Mapped[int]#
- standby: sqlalchemy.orm.Mapped[list[str]]#
- started_at: sqlalchemy.orm.Mapped[datetime.datetime]#
- success: sqlalchemy.orm.Mapped[bool]#
- class sidequest_common.models.epgp.EpgpImportAudit#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne
POST /api/epgp/importscall: who ran it, which addon recorder it came from, and what changed.- counts: sqlalchemy.orm.Mapped[dict]#
- member_id: sqlalchemy.orm.Mapped[int | None]#
- recorder: sqlalchemy.orm.Mapped[str]#
- class sidequest_common.models.epgp.EpgpLedgerEntry#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne EP or GP change: an addon-exported award, a loot GP charge, or a site-made officer adjustment.
external_idisnull(and so excluded from the unique constraint’s enforcement across site rows) for adjustments made directly on the site (POST /api/epgp/adjustments); addon-exported rows always carry it, and re-importing the same row is a no-op.character_id/member_idare both nullable: an addon row ties to the character that earned it (member_idresolved through the character’s link at read time for the ledger/standings), while a site adjustment ties directly to the target member with no character.- amount: sqlalchemy.orm.Mapped[int]#
- at: sqlalchemy.orm.Mapped[datetime.datetime]#
- character_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- external_id: sqlalchemy.orm.Mapped[str | None]#
- kind: sqlalchemy.orm.Mapped[str]#
- loot_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- member_id: sqlalchemy.orm.Mapped[int | None]#
- note: sqlalchemy.orm.Mapped[str | None]#
- officer: sqlalchemy.orm.Mapped[str]#
- reason: sqlalchemy.orm.Mapped[str]#
- session_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- source: sqlalchemy.orm.Mapped[str]#
- voided_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
- class sidequest_common.models.epgp.EpgpSyncToken#
Bases:
advanced_alchemy.base.UUIDAuditBaseA long-lived bearer token for the
sidequest-syncuploader, scoped to one member.token_hashis the sha256 hex digest of the token (formatsqs_+ 40 url-safe chars); the plaintext is shown to the officer once, at creation, and never stored. The import endpoint accepts the token only while its member still holds an officer role, checked at request time (not cached on the row).- last_used_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
- member_id: sqlalchemy.orm.Mapped[int]#
- name: sqlalchemy.orm.Mapped[str]#
- revoked_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
- token_hash: sqlalchemy.orm.Mapped[str]#
- class sidequest_common.models.epgp.LootAward#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne item awarded during a raid session, with the GP charge recorded as a twin
EpgpLedgerEntry.officeris the awarding officer’s in-game character name (addon data, not a site member link).- at: sqlalchemy.orm.Mapped[datetime.datetime]#
- character_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- encounter_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- external_id: sqlalchemy.orm.Mapped[str]#
- gp: sqlalchemy.orm.Mapped[int]#
- inv_type: sqlalchemy.orm.Mapped[str]#
- item_id: sqlalchemy.orm.Mapped[int]#
- item_name: sqlalchemy.orm.Mapped[str]#
- officer: sqlalchemy.orm.Mapped[str]#
- quality: sqlalchemy.orm.Mapped[int]#
- response: sqlalchemy.orm.Mapped[str]#
- session_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- voided_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
- class sidequest_common.models.epgp.RaidSession#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne raid night, as recorded by the addon’s session recorder and exported by
id.event_idlinks to the site’sGuildEventwhen the officer started it from a scheduled raid (drives the attendance hand-off);nullfor an ad-hoc session with no matching event.recorderis the officer character that exported the session, used for the one-recorder-per-raid-night conflict rule.- difficulty_id: sqlalchemy.orm.Mapped[int]#
- ended_at: sqlalchemy.orm.Mapped[datetime.datetime]#
- event_id: sqlalchemy.orm.Mapped[uuid.UUID | None]#
- external_id: sqlalchemy.orm.Mapped[str]#
- instance_id: sqlalchemy.orm.Mapped[int]#
- instance_name: sqlalchemy.orm.Mapped[str]#
- recorder: sqlalchemy.orm.Mapped[str]#
- size: sqlalchemy.orm.Mapped[int]#
- started_at: sqlalchemy.orm.Mapped[datetime.datetime]#
- class sidequest_common.models.epgp.SessionCharacterPresence#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne character’s attendance window within one raid session, standby (bench) or not.
Drives the attendance hand-off: every member linked to a session character (standby included, as bench) gets marked attended on the linked event’s sign-up.
- character_id: sqlalchemy.orm.Mapped[uuid.UUID]#
- first_seen: sqlalchemy.orm.Mapped[datetime.datetime]#
- last_seen: sqlalchemy.orm.Mapped[datetime.datetime]#
- session_id: sqlalchemy.orm.Mapped[uuid.UUID]#
- standby: sqlalchemy.orm.Mapped[bool]#
- class sidequest_common.models.epgp.WowCharacter#
Bases:
advanced_alchemy.base.UUIDAuditBaseOne in-game character, keyed
"name-realm"lowercase (case-insensitive match per the contract).member_idlinks the character to a site member, sharing that member’s EP/GP balance;sourcerecords how the link happened (claim: the member self-claimed it on the account page;officer: an officer set it on the characters admin screen;signup: auto-linked because the name matched exactly one member’s event sign-up character;import: seen in an addon import with no link yet).is_mainflags the character shown as a member’s primary on standings.- is_main: sqlalchemy.orm.Mapped[bool]#
- key: sqlalchemy.orm.Mapped[str]#
- last_seen_at: sqlalchemy.orm.Mapped[datetime.datetime | None]#
- member_id: sqlalchemy.orm.Mapped[int | None]#
- name: sqlalchemy.orm.Mapped[str]#
- realm: sqlalchemy.orm.Mapped[str]#
- source: sqlalchemy.orm.Mapped[str]#
- wow_class: sqlalchemy.orm.Mapped[str | None]#