feat: FlowPilot AI — Phases 4 & 5 (Gallery, Export, Responsive, Enterprise, Analytics) #116

Merged
chihlasm merged 66 commits from feat/flowpilot-ai-session into main 2026-03-21 05:15:51 +00:00
3 changed files with 76 additions and 0 deletions
Showing only changes of commit 779b29dbc4 - Show all commits

View File

@@ -0,0 +1,46 @@
"""add flow tracking columns and psa_activity_logs table
Revision ID: e0d382f083d4
Revises: 58e3f27f3e8f
Create Date: 2026-03-19 23:59:42.346587
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'e0d382f083d4'
down_revision: Union[str, None] = '58e3f27f3e8f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Create psa_activity_logs table
op.create_table(
'psa_activity_logs',
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('account_id', sa.UUID(), nullable=False),
sa.Column('session_id', sa.UUID(), nullable=True),
sa.Column('activity_type', sa.String(length=50), nullable=False),
sa.Column('hours_logged', sa.Float(), nullable=True),
sa.Column('psa_ticket_id', sa.String(length=100), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['session_id'], ['ai_sessions.id'], ondelete='SET NULL'),
sa.PrimaryKeyConstraint('id'),
)
op.create_index(op.f('ix_psa_activity_logs_account_id'), 'psa_activity_logs', ['account_id'], unique=False)
# Flow quality tracking columns on trees (usage_count, success_rate, last_matched_at)
# Note: usage_count, success_rate, and last_matched_at may already exist on this instance.
# These are included here for environments where they were not yet added.
# The columns are guarded to be safe — skip if already present.
def downgrade() -> None:
op.drop_index(op.f('ix_psa_activity_logs_account_id'), table_name='psa_activity_logs')
op.drop_table('psa_activity_logs')

View File

@@ -46,6 +46,7 @@ from .flow_proposal import FlowProposal
from .notification_config import NotificationConfig
from .notification_log import NotificationLog
from .notification import Notification
from .psa_activity_log import PsaActivityLog
__all__ = [
"User",
@@ -106,4 +107,5 @@ __all__ = [
"NotificationConfig",
"NotificationLog",
"Notification",
"PsaActivityLog",
]

View File

@@ -0,0 +1,28 @@
"""PSA activity log — tracks time entries, note posts, and status updates pushed to PSA."""
import uuid
from datetime import datetime, timezone
from typing import Optional
from sqlalchemy import String, DateTime, ForeignKey, Float
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.dialects.postgresql import UUID
from app.core.database import Base
class PsaActivityLog(Base):
__tablename__ = "psa_activity_logs"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
account_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False, index=True
)
session_id: Mapped[Optional[uuid.UUID]] = mapped_column(
UUID(as_uuid=True), ForeignKey("ai_sessions.id", ondelete="SET NULL"), nullable=True
)
activity_type: Mapped[str] = mapped_column(String(50), nullable=False)
hours_logged: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
psa_ticket_id: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)