Add poll workflow and signed participation support

This commit is contained in:
2026-07-12 17:32:07 +02:00
parent d0e68f5aa6
commit fc9b7c287c
10 changed files with 519 additions and 16 deletions
@@ -0,0 +1,68 @@
"""v0.1.8 poll workflow context and invitations
Revision ID: 2a3b4c5d6e7f
Revises: 1f2e3d4c5b6a
Create Date: 2026-07-12 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "2a3b4c5d6e7f"
down_revision = "1f2e3d4c5b6a"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column("poll_polls", sa.Column("context_module", sa.String(length=100), nullable=True))
op.add_column("poll_polls", sa.Column("context_resource_type", sa.String(length=100), nullable=True))
op.add_column("poll_polls", sa.Column("context_resource_id", sa.String(length=255), nullable=True))
op.add_column("poll_polls", sa.Column("workflow_state", sa.String(length=100), nullable=True))
op.add_column("poll_polls", sa.Column("workflow_steps", sa.JSON(), nullable=False, server_default="[]"))
op.create_index(op.f("ix_poll_polls_context_module"), "poll_polls", ["context_module"], unique=False)
op.create_index(op.f("ix_poll_polls_context_resource_id"), "poll_polls", ["context_resource_id"], unique=False)
op.create_index(op.f("ix_poll_polls_workflow_state"), "poll_polls", ["workflow_state"], unique=False)
op.create_table(
"poll_invitations",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=False),
sa.Column("poll_id", sa.String(length=36), nullable=False),
sa.Column("token_hash", sa.String(length=64), nullable=False),
sa.Column("respondent_id", sa.String(length=255), nullable=True),
sa.Column("respondent_label", sa.String(length=500), nullable=True),
sa.Column("email", sa.String(length=320), nullable=True),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("metadata", sa.JSON(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(["poll_id"], ["poll_polls.id"], name=op.f("fk_poll_invitations_poll_id_poll_polls"), ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_poll_invitations")),
sa.UniqueConstraint("token_hash", name="uq_poll_invitations_token_hash"),
)
op.create_index(op.f("ix_poll_invitations_email"), "poll_invitations", ["email"], unique=False)
op.create_index(op.f("ix_poll_invitations_expires_at"), "poll_invitations", ["expires_at"], unique=False)
op.create_index(op.f("ix_poll_invitations_poll_id"), "poll_invitations", ["poll_id"], unique=False)
op.create_index("ix_poll_invitations_poll", "poll_invitations", ["tenant_id", "poll_id"], unique=False)
op.create_index("ix_poll_invitations_poll_email", "poll_invitations", ["poll_id", "email"], unique=False)
op.create_index(op.f("ix_poll_invitations_respondent_id"), "poll_invitations", ["respondent_id"], unique=False)
op.create_index(op.f("ix_poll_invitations_revoked_at"), "poll_invitations", ["revoked_at"], unique=False)
op.create_index(op.f("ix_poll_invitations_tenant_id"), "poll_invitations", ["tenant_id"], unique=False)
op.create_index(op.f("ix_poll_invitations_token_hash"), "poll_invitations", ["token_hash"], unique=False)
def downgrade() -> None:
op.drop_table("poll_invitations")
op.drop_index(op.f("ix_poll_polls_workflow_state"), table_name="poll_polls")
op.drop_index(op.f("ix_poll_polls_context_resource_id"), table_name="poll_polls")
op.drop_index(op.f("ix_poll_polls_context_module"), table_name="poll_polls")
op.drop_column("poll_polls", "workflow_steps")
op.drop_column("poll_polls", "workflow_state")
op.drop_column("poll_polls", "context_resource_id")
op.drop_column("poll_polls", "context_resource_type")
op.drop_column("poll_polls", "context_module")