From ca7ca65c25de12a00959afc811dffbc8c8d62f62 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Thu, 5 Mar 2026 01:41:22 -0500 Subject: [PATCH] feat: add survey_invites migration and invite_id FK Co-Authored-By: Claude Opus 4.6 --- .../versions/047_add_survey_invites_table.py | 39 +++++++++++++++++++ backend/app/models/survey_response.py | 20 ++++++++++ 2 files changed, 59 insertions(+) create mode 100644 backend/alembic/versions/047_add_survey_invites_table.py create mode 100644 backend/app/models/survey_response.py diff --git a/backend/alembic/versions/047_add_survey_invites_table.py b/backend/alembic/versions/047_add_survey_invites_table.py new file mode 100644 index 00000000..ef9e61d6 --- /dev/null +++ b/backend/alembic/versions/047_add_survey_invites_table.py @@ -0,0 +1,39 @@ +"""Add survey_invites table and invite_id FK on survey_responses. + +Revision ID: 047 +Revises: 046 +Create Date: 2026-03-04 +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import UUID + +revision: str = "047" +down_revision: str = "046" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_table( + "survey_invites", + sa.Column("id", UUID(as_uuid=True), primary_key=True), + sa.Column("token", sa.String(32), unique=True, nullable=False), + sa.Column("recipient_name", sa.String(255), nullable=False), + sa.Column("recipient_email", sa.String(255), nullable=True), + sa.Column("status", sa.String(20), nullable=False, server_default="pending"), + sa.Column("email_sent", sa.Boolean, nullable=False, server_default="false"), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True), + ) + op.add_column( + "survey_responses", + sa.Column("invite_id", UUID(as_uuid=True), sa.ForeignKey("survey_invites.id"), nullable=True), + ) + + +def downgrade() -> None: + op.drop_column("survey_responses", "invite_id") + op.drop_table("survey_invites") diff --git a/backend/app/models/survey_response.py b/backend/app/models/survey_response.py new file mode 100644 index 00000000..36a29da4 --- /dev/null +++ b/backend/app/models/survey_response.py @@ -0,0 +1,20 @@ +"""FlowPilot survey response storage.""" +import uuid +from datetime import datetime, timezone + +from sqlalchemy import Column, DateTime, ForeignKey, String, Text +from sqlalchemy.dialects.postgresql import JSONB, UUID + +from app.core.database import Base + + +class SurveyResponse(Base): + __tablename__ = "survey_responses" + + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + respondent_name = Column(String(255), nullable=True) + responses = Column(JSONB, nullable=False) + ip_address = Column(String(45), nullable=True) + user_agent = Column(Text, nullable=True) + invite_id = Column(UUID(as_uuid=True), ForeignKey("survey_invites.id"), nullable=True) + created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)