sidequest_common.models.guild_hub ================================= .. py:module:: sidequest_common.models.guild_hub .. autoapi-nested-parse:: Guild hub v2: roster snapshot, professions/recipes, bank log, announcements, recruiting, notes, news. Backs the addon contract's v2 sections (``local://addon-contract-v2.md``): the ``SQX1:`` export's ``roster``, ``professions``, ``bank``, ``announcements``, ``recruits``, and ``member_notes`` arrays, read back through ``/api/guild/*`` and the officer recruiting/notes screens, and mirrored into the ``SQI1:`` game-import string. ``GuildNewsEvent`` is generated (not addon-sourced) at the moment each underlying event is recorded, so the news feed has stable ids without re-deriving (and possibly reordering) them from other tables on every read. Classes ------- .. autoapisummary:: sidequest_common.models.guild_hub.CharacterProfession sidequest_common.models.guild_hub.CharacterRecipeLink sidequest_common.models.guild_hub.GuildAnnouncement sidequest_common.models.guild_hub.GuildBankLogEntry sidequest_common.models.guild_hub.GuildNewsEvent sidequest_common.models.guild_hub.GuildRecruit sidequest_common.models.guild_hub.GuildRosterSnapshot sidequest_common.models.guild_hub.MemberNote sidequest_common.models.guild_hub.ProfessionRecipe sidequest_common.models.guild_hub.RecruitVote Module Contents --------------- .. py:class:: CharacterProfession Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One character's scanned profession (e.g. Blacksmithing 150/150); ``recipes`` replaced wholesale on each scan. .. py:attribute:: character_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: max_skill :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: profession :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: scanned_at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: skill :type: sqlalchemy.orm.Mapped[int] .. py:class:: CharacterRecipeLink Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` Many-to-many: one character/profession pair knows one recipe. .. py:attribute:: profession_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: recipe_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:class:: GuildAnnouncement Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One officer announcement. From the addon (``external_id`` set, ``discord: true`` queues it for the bot) or made directly on the site (``external_id`` ``None``, ``source="site"``). .. py:attribute:: at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: author :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: discord :type: sqlalchemy.orm.Mapped[bool] .. py:attribute:: discord_message_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: external_id :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: posted_at :type: sqlalchemy.orm.Mapped[datetime.datetime | None] .. py:attribute:: source :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: text :type: sqlalchemy.orm.Mapped[str] .. py:class:: GuildBankLogEntry Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One guild bank log row. ``external_id`` is the addon's deterministic hash (the game's log has no ids). .. py:attribute:: at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: character_id :type: sqlalchemy.orm.Mapped[uuid.UUID | None] .. py:attribute:: count :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: external_id :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: item_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: item_name :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: kind :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: money :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: tab :type: sqlalchemy.orm.Mapped[int | None] .. py:class:: GuildNewsEvent Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One news feed item, generated once when its source event is first recorded. ``source_key`` is a deterministic string built from the source row (e.g. ``"loot:"``, ``"level::"``); its uniqueness makes re-importing the same addon data a no-op instead of duplicating news, and gives the addon a stable id (the row's own ``id``) across imports. .. py:attribute:: at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: character :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: item_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: kind :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: quality :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: source_key :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: text :type: sqlalchemy.orm.Mapped[str] .. py:class:: GuildRecruit Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One recruiting candidate (officer-only data), upserted by the addon's ``id`` or created on the site. .. py:attribute:: at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: character :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: checklist :type: sqlalchemy.orm.Mapped[dict] .. py:attribute:: external_id :type: sqlalchemy.orm.Mapped[str | None] .. py:attribute:: note :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:: GuildRosterSnapshot Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` The latest ``GetGuildRosterInfo`` row for one character, upserted on every roster export. One row per character (``character_id`` unique): the addon only ever reports its latest sweep, not history. ``left_guild_at`` is set once a character goes 30 days without appearing in a roster export; never deleted. .. py:attribute:: character_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: last_seen_at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: left_guild_at :type: sqlalchemy.orm.Mapped[datetime.datetime | None] .. py:attribute:: level :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: rank :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: rank_index :type: sqlalchemy.orm.Mapped[int] .. py:attribute:: zone :type: sqlalchemy.orm.Mapped[str] .. py:class:: MemberNote Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One officer note about a character, voidable, never deleted. ``member_id`` resolves via the character link. .. py:attribute:: at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: character :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: external_id :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: member_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: note :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: officer :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: voided_at :type: sqlalchemy.orm.Mapped[datetime.datetime | None] .. py:class:: ProfessionRecipe Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` A known recipe, keyed by the game's recipe id (the contract's ``recipes[].id``), shared across characters. .. py:attribute:: item_id :type: sqlalchemy.orm.Mapped[int | None] .. py:attribute:: name :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: recipe_id :type: sqlalchemy.orm.Mapped[int] .. py:class:: RecruitVote Bases: :py:obj:`advanced_alchemy.base.UUIDAuditBase` One officer's vote on one recruit, upserted by ``(recruit, officer)``. .. py:attribute:: at :type: sqlalchemy.orm.Mapped[datetime.datetime] .. py:attribute:: officer :type: sqlalchemy.orm.Mapped[str] .. py:attribute:: recruit_id :type: sqlalchemy.orm.Mapped[uuid.UUID] .. py:attribute:: vote :type: sqlalchemy.orm.Mapped[str]