Spaces:
Running
Running
| from __future__ import annotations | |
| from datetime import datetime, timezone | |
| from uuid import uuid4 | |
| from sqlalchemy import ( | |
| JSON, | |
| BigInteger, | |
| DateTime, | |
| ForeignKey, | |
| Index, | |
| Integer, | |
| String, | |
| Text, | |
| UniqueConstraint, | |
| ) | |
| from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column | |
| def utcnow() -> datetime: | |
| return datetime.now(timezone.utc) | |
| def new_id() -> str: | |
| return str(uuid4()) | |
| class SocialBase(DeclarativeBase): | |
| """Separate metadata keeps production schema changes migration-only.""" | |
| class SocialAccount(SocialBase): | |
| __tablename__ = "social_accounts" | |
| __table_args__ = ( | |
| UniqueConstraint( | |
| "workspace_id", | |
| "provider", | |
| "external_account_id", | |
| name="uq_social_account_workspace_provider_external", | |
| ), | |
| Index("ix_social_accounts_workspace_id", "workspace_id"), | |
| Index("ix_social_accounts_provider_status", "provider", "status"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| provider: Mapped[str] = mapped_column(String(32), nullable=False) | |
| account_type: Mapped[str] = mapped_column(String(64), nullable=False) | |
| external_account_id: Mapped[str] = mapped_column(String(255), nullable=False) | |
| username: Mapped[str | None] = mapped_column(String(255)) | |
| display_name: Mapped[str | None] = mapped_column(String(255)) | |
| avatar_url: Mapped[str | None] = mapped_column(String(2048)) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="pending") | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| last_synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| class SocialAccountToken(SocialBase): | |
| __tablename__ = "social_account_tokens" | |
| __table_args__ = ( | |
| UniqueConstraint("social_account_id", name="uq_social_account_token"), | |
| Index("ix_social_account_tokens_expires_at", "expires_at"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_account_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_accounts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| access_token_secret_id: Mapped[str | None] = mapped_column(String(255)) | |
| refresh_token_secret_id: Mapped[str | None] = mapped_column(String(255)) | |
| encrypted_payload: Mapped[str | None] = mapped_column(Text) | |
| expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| scopes: Mapped[list[str]] = mapped_column(JSON, nullable=False, default=list) | |
| token_type: Mapped[str | None] = mapped_column(String(64)) | |
| last_refreshed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| class SocialAccountCapability(SocialBase): | |
| __tablename__ = "social_account_capabilities" | |
| __table_args__ = ( | |
| UniqueConstraint("social_account_id", "capability", name="uq_social_account_capability"), | |
| Index("ix_social_account_capabilities_account", "social_account_id"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_account_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_accounts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| capability: Mapped[str] = mapped_column(String(100), nullable=False) | |
| enabled: Mapped[bool] = mapped_column(nullable=False, default=False) | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| class MediaVariant(SocialBase): | |
| __tablename__ = "media_variants" | |
| __table_args__ = ( | |
| Index("ix_media_variants_workspace_source", "workspace_id", "source_asset_id"), | |
| Index("ix_media_variants_platform", "platform"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| source_asset_id: Mapped[str] = mapped_column(String(255), nullable=False) | |
| asset_reference: Mapped[str | None] = mapped_column(String(2048)) | |
| template_id: Mapped[str | None] = mapped_column(String(255)) | |
| platform: Mapped[str | None] = mapped_column(String(32)) | |
| width: Mapped[int | None] = mapped_column(Integer) | |
| height: Mapped[int | None] = mapped_column(Integer) | |
| duration_seconds: Mapped[float | None] = mapped_column() | |
| codec: Mapped[str | None] = mapped_column(String(64)) | |
| container: Mapped[str | None] = mapped_column(String(64)) | |
| bitrate: Mapped[int | None] = mapped_column(BigInteger) | |
| file_size: Mapped[int | None] = mapped_column(BigInteger) | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| class SocialMediaAsset(SocialBase): | |
| """A workspace-owned reference to an output published by MediaRouter. | |
| The media processor remains the source of truth for files. This table | |
| only records a tenant binding and immutable download locator so social | |
| workers can validate and stream the exact output without trusting a | |
| caller-supplied filesystem path or URL. | |
| """ | |
| __tablename__ = "social_media_assets" | |
| __table_args__ = ( | |
| UniqueConstraint( | |
| "workspace_id", "request_id", "filename", name="uq_social_media_asset_workspace_output" | |
| ), | |
| Index("ix_social_media_assets_workspace_id", "workspace_id"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| # Canonical ownership lives in the security/asset domain. This local | |
| # social binding is cacheable publishing metadata, not ownership proof. | |
| canonical_asset_id: Mapped[str | None] = mapped_column(String(36), nullable=True) | |
| request_id: Mapped[str] = mapped_column(String(36), nullable=False) | |
| filename: Mapped[str] = mapped_column(String(255), nullable=False) | |
| mime_type: Mapped[str] = mapped_column(String(255), nullable=False) | |
| file_size: Mapped[int] = mapped_column(BigInteger, nullable=False) | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| class SocialCampaign(SocialBase): | |
| __tablename__ = "social_campaigns" | |
| __table_args__ = (Index("ix_social_campaigns_workspace_id", "workspace_id"),) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| name: Mapped[str] = mapped_column(String(255), nullable=False) | |
| description: Mapped[str | None] = mapped_column(Text) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="draft") | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| class SocialPost(SocialBase): | |
| __tablename__ = "social_posts" | |
| __table_args__ = ( | |
| UniqueConstraint( | |
| "workspace_id", "idempotency_key", name="uq_social_posts_workspace_idempotency" | |
| ), | |
| Index("ix_social_posts_workspace_created", "workspace_id", "created_at"), | |
| Index("ix_social_posts_status", "status"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| project_id: Mapped[str | None] = mapped_column(String(36), nullable=True) | |
| campaign_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("social_campaigns.id", ondelete="SET NULL") | |
| ) | |
| media_asset_id: Mapped[str | None] = mapped_column(String(255), nullable=True) | |
| canonical_caption: Mapped[str | None] = mapped_column(Text) | |
| canonical_hashtags: Mapped[list[str]] = mapped_column(JSON, nullable=False, default=list) | |
| brand_kit_version_id: Mapped[str | None] = mapped_column(String(36), nullable=True) | |
| source_variant_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("media_variants.id", ondelete="SET NULL") | |
| ) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="draft") | |
| publish_mode: Mapped[str] = mapped_column(String(32), nullable=False, default="draft") | |
| idempotency_key: Mapped[str | None] = mapped_column(String(255)) | |
| request_fingerprint: Mapped[str | None] = mapped_column(String(64)) | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_by: Mapped[str | None] = mapped_column(String(120)) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| published_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| revision: Mapped[int] = mapped_column(Integer, nullable=False, default=1) | |
| class SocialPostTarget(SocialBase): | |
| __tablename__ = "social_post_targets" | |
| __table_args__ = ( | |
| UniqueConstraint("social_post_id", "social_account_id", name="uq_social_post_target"), | |
| Index("ix_social_post_targets_post", "social_post_id"), | |
| Index("ix_social_post_targets_account", "social_account_id"), | |
| Index("ix_social_post_targets_status", "status"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_post_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_posts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| social_account_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_accounts.id", ondelete="RESTRICT"), nullable=False | |
| ) | |
| provider: Mapped[str] = mapped_column(String(32), nullable=False) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="draft") | |
| caption_json: Mapped[dict[str, object]] = mapped_column( | |
| "caption", JSON, nullable=False, default=dict | |
| ) | |
| platform_metadata: Mapped[dict[str, object]] = mapped_column(JSON, nullable=False, default=dict) | |
| external_post_id: Mapped[str | None] = mapped_column(String(255)) | |
| external_url: Mapped[str | None] = mapped_column(String(2048)) | |
| error_code: Mapped[str | None] = mapped_column(String(100)) | |
| error_message: Mapped[str | None] = mapped_column(Text) | |
| published_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| cancellation_requested_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| class SocialPostMedia(SocialBase): | |
| __tablename__ = "social_post_media" | |
| __table_args__ = (Index("ix_social_post_media_post", "social_post_id"),) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_post_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_posts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| media_variant_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("media_variants.id", ondelete="SET NULL") | |
| ) | |
| media_asset_id: Mapped[str | None] = mapped_column(String(255)) | |
| position: Mapped[int] = mapped_column(Integer, nullable=False, default=0) | |
| kind: Mapped[str] = mapped_column(String(32), nullable=False, default="video") | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| class SocialSchedule(SocialBase): | |
| __tablename__ = "social_schedules" | |
| __table_args__ = ( | |
| UniqueConstraint("social_post_id", name="uq_social_schedule_post"), | |
| Index("ix_social_schedules_due", "status", "scheduled_at"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_post_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_posts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| scheduled_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) | |
| timezone: Mapped[str] = mapped_column(String(100), nullable=False) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="scheduled") | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| revision: Mapped[int] = mapped_column(Integer, nullable=False, default=1) | |
| class SocialJob(SocialBase): | |
| __tablename__ = "social_jobs" | |
| __table_args__ = ( | |
| UniqueConstraint( | |
| "workspace_id", "idempotency_key", name="uq_social_jobs_workspace_idempotency" | |
| ), | |
| Index("ix_social_jobs_workspace_status", "workspace_id", "status"), | |
| Index("ix_social_jobs_next_attempt", "status", "next_attempt_at"), | |
| Index("ix_social_jobs_post", "social_post_id"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| social_post_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_posts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| social_post_target_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("social_post_targets.id", ondelete="CASCADE") | |
| ) | |
| provider: Mapped[str | None] = mapped_column(String(32)) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued") | |
| attempt_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) | |
| max_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=5) | |
| next_attempt_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| idempotency_key: Mapped[str | None] = mapped_column(String(255)) | |
| error_code: Mapped[str | None] = mapped_column(String(100)) | |
| error_message: Mapped[str | None] = mapped_column(Text) | |
| payload_json: Mapped[dict[str, object]] = mapped_column( | |
| "payload", JSON, nullable=False, default=dict | |
| ) | |
| # Provider resumable-session URLs are bearer-like credentials. They must | |
| # survive a worker restart but must never be present in job REST/MCP/SDK | |
| # payloads, so they are encrypted separately from payload JSON. | |
| provider_state_encrypted: Mapped[str | None] = mapped_column(Text) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| cancellation_requested_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |
| class SocialJobAttempt(SocialBase): | |
| __tablename__ = "social_job_attempts" | |
| __table_args__ = ( | |
| UniqueConstraint("social_job_id", "attempt_number", name="uq_social_job_attempt_number"), | |
| Index("ix_social_job_attempts_job", "social_job_id", "attempt_number"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_job_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_jobs.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| attempt_number: Mapped[int] = mapped_column(Integer, nullable=False) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False) | |
| error_code: Mapped[str | None] = mapped_column(String(100)) | |
| error_message: Mapped[str | None] = mapped_column(Text) | |
| provider_request_id: Mapped[str | None] = mapped_column(String(255)) | |
| started_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| class OAuthState(SocialBase): | |
| __tablename__ = "oauth_states" | |
| __table_args__ = ( | |
| Index("ix_oauth_states_state", "state", unique=True), | |
| Index("ix_oauth_states_expires_at", "expires_at"), | |
| Index("ix_oauth_states_workspace", "workspace_id"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| state: Mapped[str] = mapped_column(String(255), nullable=False) | |
| provider: Mapped[str] = mapped_column(String(32), nullable=False) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| user_id: Mapped[str | None] = mapped_column(String(120)) | |
| redirect_uri: Mapped[str] = mapped_column(String(2048), nullable=False) | |
| requested_account_type: Mapped[str | None] = mapped_column(String(64)) | |
| requested_scopes: Mapped[list[str]] = mapped_column(JSON, nullable=False, default=list) | |
| code_verifier_encrypted: Mapped[str | None] = mapped_column(Text) | |
| expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) | |
| used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| class SocialWebhookEvent(SocialBase): | |
| __tablename__ = "social_webhook_events" | |
| __table_args__ = ( | |
| UniqueConstraint( | |
| "provider", "external_event_id", name="uq_social_webhook_provider_external" | |
| ), | |
| Index("ix_social_webhook_events_status", "status"), | |
| Index("ix_social_webhook_events_received", "received_at"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| provider: Mapped[str] = mapped_column(String(32), nullable=False) | |
| event_type: Mapped[str] = mapped_column(String(100), nullable=False) | |
| external_event_id: Mapped[str] = mapped_column(String(255), nullable=False) | |
| workspace_id: Mapped[str | None] = mapped_column(String(120)) | |
| payload_json: Mapped[dict[str, object]] = mapped_column( | |
| "payload", JSON, nullable=False, default=dict | |
| ) | |
| received_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| processed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="received") | |
| error_message: Mapped[str | None] = mapped_column(Text) | |
| class SocialPostMetric(SocialBase): | |
| __tablename__ = "social_post_metrics" | |
| __table_args__ = ( | |
| Index("ix_social_post_metrics_target_retrieved", "social_post_target_id", "retrieved_at"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| social_post_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_posts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| social_post_target_id: Mapped[str | None] = mapped_column( | |
| String(36), ForeignKey("social_post_targets.id", ondelete="CASCADE") | |
| ) | |
| provider: Mapped[str] = mapped_column(String(32), nullable=False) | |
| views: Mapped[int | None] = mapped_column(BigInteger) | |
| impressions: Mapped[int | None] = mapped_column(BigInteger) | |
| likes: Mapped[int | None] = mapped_column(BigInteger) | |
| comments: Mapped[int | None] = mapped_column(BigInteger) | |
| shares: Mapped[int | None] = mapped_column(BigInteger) | |
| engagement_rate: Mapped[float | None] = mapped_column() | |
| published_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| retrieved_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| raw_metrics: Mapped[dict[str, object]] = mapped_column(JSON, nullable=False, default=dict) | |
| class SocialAuditEvent(SocialBase): | |
| __tablename__ = "social_audit_events" | |
| __table_args__ = ( | |
| Index("ix_social_audit_events_workspace_created", "workspace_id", "created_at"), | |
| Index("ix_social_audit_events_type", "event_type"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| api_key_id: Mapped[str | None] = mapped_column(String(36)) | |
| event_type: Mapped[str] = mapped_column(String(100), nullable=False) | |
| provider: Mapped[str | None] = mapped_column(String(32)) | |
| social_account_id: Mapped[str | None] = mapped_column(String(36)) | |
| social_post_id: Mapped[str | None] = mapped_column(String(36)) | |
| social_job_id: Mapped[str | None] = mapped_column(String(36)) | |
| request_id: Mapped[str | None] = mapped_column(String(64)) | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| class SocialPublishingBatch(SocialBase): | |
| __tablename__ = "social_publishing_batches" | |
| __table_args__ = ( | |
| UniqueConstraint( | |
| "workspace_id", "idempotency_key", name="uq_social_publishing_batch_idempotency" | |
| ), | |
| Index("ix_social_publishing_batches_workspace_status", "workspace_id", "status"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| workspace_id: Mapped[str] = mapped_column(String(120), nullable=False) | |
| operation: Mapped[str] = mapped_column(String(32), nullable=False) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued") | |
| idempotency_key: Mapped[str] = mapped_column(String(255), nullable=False) | |
| requested_by: Mapped[str | None] = mapped_column(String(120)) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) | |
| class SocialPublishingBatchItem(SocialBase): | |
| __tablename__ = "social_publishing_batch_items" | |
| __table_args__ = ( | |
| UniqueConstraint("batch_id", "social_post_id", name="uq_social_publishing_batch_item"), | |
| Index("ix_social_publishing_batch_items_status", "status"), | |
| ) | |
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id) | |
| batch_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_publishing_batches.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| social_post_id: Mapped[str] = mapped_column( | |
| String(36), ForeignKey("social_posts.id", ondelete="CASCADE"), nullable=False | |
| ) | |
| status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued") | |
| error_code: Mapped[str | None] = mapped_column(String(100)) | |
| error_message: Mapped[str | None] = mapped_column(Text) | |
| metadata_json: Mapped[dict[str, object]] = mapped_column( | |
| "metadata", JSON, nullable=False, default=dict | |
| ) | |
| created_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow | |
| ) | |
| updated_at: Mapped[datetime] = mapped_column( | |
| DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow | |
| ) | |