Add initial polling backend logic

This commit is contained in:
2026-07-12 16:26:45 +02:00
parent ddf1fcc217
commit d0e68f5aa6
12 changed files with 1325 additions and 0 deletions
@@ -0,0 +1 @@
from __future__ import annotations
@@ -0,0 +1,120 @@
"""v0.1.8 poll baseline
Revision ID: 1f2e3d4c5b6a
Revises: None
Create Date: 2026-07-12 00:00:00.000000
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "1f2e3d4c5b6a"
down_revision = None
branch_labels = None
depends_on = "4f2a9c8e7b6d"
def upgrade() -> None:
op.create_table(
"poll_polls",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("tenant_id", sa.String(length=36), nullable=False),
sa.Column("slug", sa.String(length=120), nullable=False),
sa.Column("title", sa.String(length=500), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("kind", sa.String(length=40), nullable=False),
sa.Column("status", sa.String(length=30), nullable=False),
sa.Column("visibility", sa.String(length=30), nullable=False),
sa.Column("result_visibility", sa.String(length=30), nullable=False),
sa.Column("allow_anonymous", sa.Boolean(), nullable=False),
sa.Column("allow_response_update", sa.Boolean(), nullable=False),
sa.Column("min_choices", sa.Integer(), nullable=False),
sa.Column("max_choices", sa.Integer(), nullable=True),
sa.Column("opens_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("closes_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("closed_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("decided_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("decided_option_id", sa.String(length=36), nullable=True),
sa.Column("created_by_user_id", sa.String(length=36), nullable=True),
sa.Column("deleted_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.PrimaryKeyConstraint("id", name=op.f("pk_poll_polls")),
)
op.create_index(op.f("ix_poll_polls_closes_at"), "poll_polls", ["closes_at"], unique=False)
op.create_index(op.f("ix_poll_polls_created_by_user_id"), "poll_polls", ["created_by_user_id"], unique=False)
op.create_index(op.f("ix_poll_polls_decided_option_id"), "poll_polls", ["decided_option_id"], unique=False)
op.create_index(op.f("ix_poll_polls_deleted_at"), "poll_polls", ["deleted_at"], unique=False)
op.create_index(op.f("ix_poll_polls_kind"), "poll_polls", ["kind"], unique=False)
op.create_index(op.f("ix_poll_polls_opens_at"), "poll_polls", ["opens_at"], unique=False)
op.create_index(op.f("ix_poll_polls_status"), "poll_polls", ["status"], unique=False)
op.create_index(op.f("ix_poll_polls_tenant_id"), "poll_polls", ["tenant_id"], unique=False)
op.create_index(op.f("ix_poll_polls_visibility"), "poll_polls", ["visibility"], unique=False)
op.create_index("ix_poll_polls_tenant_kind", "poll_polls", ["tenant_id", "kind"], unique=False)
op.create_index("ix_poll_polls_tenant_status", "poll_polls", ["tenant_id", "status"], unique=False)
op.create_index("ix_poll_polls_window", "poll_polls", ["tenant_id", "opens_at", "closes_at"], unique=False)
op.create_index(
"uq_poll_polls_active_slug",
"poll_polls",
["tenant_id", "slug"],
unique=True,
sqlite_where=sa.text("deleted_at IS NULL"),
postgresql_where=sa.text("deleted_at IS NULL"),
)
op.create_table(
"poll_options",
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("key", sa.String(length=120), nullable=False),
sa.Column("label", sa.String(length=500), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("position", sa.Integer(), nullable=False),
sa.Column("value", sa.JSON(), nullable=True),
sa.Column("metadata", sa.JSON(), nullable=True),
sa.Column("deleted_at", sa.DateTime(timezone=True), 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_options_poll_id_poll_polls"), ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_poll_options")),
sa.UniqueConstraint("poll_id", "key", name="uq_poll_options_poll_key"),
)
op.create_index(op.f("ix_poll_options_deleted_at"), "poll_options", ["deleted_at"], unique=False)
op.create_index(op.f("ix_poll_options_poll_id"), "poll_options", ["poll_id"], unique=False)
op.create_index("ix_poll_options_poll_position", "poll_options", ["poll_id", "position"], unique=False)
op.create_index(op.f("ix_poll_options_tenant_id"), "poll_options", ["tenant_id"], unique=False)
op.create_table(
"poll_responses",
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("respondent_id", sa.String(length=255), nullable=True),
sa.Column("respondent_label", sa.String(length=500), nullable=True),
sa.Column("answers", sa.JSON(), nullable=False),
sa.Column("submitted_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("deleted_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_responses_poll_id_poll_polls"), ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id", name=op.f("pk_poll_responses")),
)
op.create_index(op.f("ix_poll_responses_deleted_at"), "poll_responses", ["deleted_at"], unique=False)
op.create_index(op.f("ix_poll_responses_poll_id"), "poll_responses", ["poll_id"], unique=False)
op.create_index("ix_poll_responses_poll_respondent", "poll_responses", ["poll_id", "respondent_id"], unique=False)
op.create_index("ix_poll_responses_poll_submitted", "poll_responses", ["poll_id", "submitted_at"], unique=False)
op.create_index(op.f("ix_poll_responses_respondent_id"), "poll_responses", ["respondent_id"], unique=False)
op.create_index(op.f("ix_poll_responses_submitted_at"), "poll_responses", ["submitted_at"], unique=False)
op.create_index(op.f("ix_poll_responses_tenant_id"), "poll_responses", ["tenant_id"], unique=False)
op.create_index("ix_poll_responses_tenant_poll", "poll_responses", ["tenant_id", "poll_id"], unique=False)
def downgrade() -> None:
op.drop_table("poll_responses")
op.drop_table("poll_options")
op.drop_table("poll_polls")
@@ -0,0 +1 @@
from __future__ import annotations