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#

Encounter

One boss pull (or kill) within a raid session, for the loot log and the progression board.

EpgpImportAudit

One POST /api/epgp/imports call: who ran it, which addon recorder it came from, and what changed.

EpgpLedgerEntry

One EP or GP change: an addon-exported award, a loot GP charge, or a site-made officer adjustment.

EpgpSyncToken

A long-lived bearer token for the sidequest-sync uploader, scoped to one member.

LootAward

One item awarded during a raid session, with the GP charge recorded as a twin EpgpLedgerEntry.

RaidSession

One raid night, as recorded by the addon's session recorder and exported by id.

SessionCharacterPresence

One character's attendance window within one raid session, standby (bench) or not.

WowCharacter

One in-game character, keyed "name-realm" lowercase (case-insensitive match per the contract).

Module Contents#

class sidequest_common.models.epgp.Encounter#

Bases: advanced_alchemy.base.UUIDAuditBase

One boss pull (or kill) within a raid session, for the loot log and the progression board.

characters/standby are 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’s SessionCharacterPresence rows 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.UUIDAuditBase

One POST /api/epgp/imports call: 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.UUIDAuditBase

One EP or GP change: an addon-exported award, a loot GP charge, or a site-made officer adjustment.

external_id is null (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_id are both nullable: an addon row ties to the character that earned it (member_id resolved 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.UUIDAuditBase

A long-lived bearer token for the sidequest-sync uploader, scoped to one member.

token_hash is the sha256 hex digest of the token (format sqs_ + 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.UUIDAuditBase

One item awarded during a raid session, with the GP charge recorded as a twin EpgpLedgerEntry.

officer is 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.UUIDAuditBase

One raid night, as recorded by the addon’s session recorder and exported by id.

event_id links to the site’s GuildEvent when the officer started it from a scheduled raid (drives the attendance hand-off); null for an ad-hoc session with no matching event. recorder is 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.UUIDAuditBase

One 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.UUIDAuditBase

One in-game character, keyed "name-realm" lowercase (case-insensitive match per the contract).

member_id links the character to a site member, sharing that member’s EP/GP balance; source records 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_main flags 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]#