Task 003: add postgres schema and seed data
This commit is contained in:
@@ -0,0 +1,606 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from collections.abc import Iterable
|
||||
from contextlib import closing
|
||||
|
||||
from src.domain.contracts import (
|
||||
AgentJobErrorCategory,
|
||||
AgentJobStatus,
|
||||
AgentJobType,
|
||||
ArticleWorkflowStatus,
|
||||
AssetStatus,
|
||||
AssetType,
|
||||
ClaimRiskLevel,
|
||||
ClaimSupportStatus,
|
||||
PlanReviewStatus,
|
||||
PublishingStatus,
|
||||
Role,
|
||||
ScriptConfigVersionStatus,
|
||||
)
|
||||
|
||||
|
||||
def _quoted_values(values: Iterable[str]) -> str:
|
||||
return ", ".join(f"'{value}'" for value in values)
|
||||
|
||||
|
||||
ROLE_VALUES = _quoted_values(role.value for role in Role)
|
||||
ARTICLE_STATUS_VALUES = _quoted_values(status.value for status in ArticleWorkflowStatus)
|
||||
PUBLISHING_STATUS_VALUES = _quoted_values(status.value for status in PublishingStatus)
|
||||
SCRIPT_CONFIG_STATUS_VALUES = _quoted_values(
|
||||
status.value for status in ScriptConfigVersionStatus
|
||||
)
|
||||
PLAN_REVIEW_STATUS_VALUES = _quoted_values(status.value for status in PlanReviewStatus)
|
||||
CLAIM_SUPPORT_STATUS_VALUES = _quoted_values(
|
||||
status.value for status in ClaimSupportStatus
|
||||
)
|
||||
CLAIM_RISK_LEVEL_VALUES = _quoted_values(level.value for level in ClaimRiskLevel)
|
||||
ASSET_TYPE_VALUES = _quoted_values(asset_type.value for asset_type in AssetType)
|
||||
ASSET_STATUS_VALUES = _quoted_values(status.value for status in AssetStatus)
|
||||
AGENT_JOB_TYPE_VALUES = _quoted_values(job_type.value for job_type in AgentJobType)
|
||||
AGENT_JOB_STATUS_VALUES = _quoted_values(status.value for status in AgentJobStatus)
|
||||
AGENT_JOB_ERROR_CATEGORY_VALUES = _quoted_values(
|
||||
category.value for category in AgentJobErrorCategory
|
||||
)
|
||||
|
||||
|
||||
POSTGRES_SCHEMA_STATEMENTS: tuple[str, ...] = (
|
||||
'CREATE EXTENSION IF NOT EXISTS "pgcrypto"',
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ({ROLE_VALUES})),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS target_sites (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
publishing_type TEXT NOT NULL DEFAULT 'git_next',
|
||||
default_language TEXT NOT NULL DEFAULT 'en',
|
||||
brand_voice TEXT NOT NULL,
|
||||
audience TEXT NOT NULL,
|
||||
seo_rules JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
visual_rules JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
source_rules JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
publishing_rules JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
active_script_config_version_id UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS script_config_versions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
target_site_id UUID NOT NULL REFERENCES target_sites(id) ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ({SCRIPT_CONFIG_STATUS_VALUES})),
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
diff JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
||||
rollback_target_version_id UUID REFERENCES script_config_versions(id),
|
||||
activated_at TIMESTAMPTZ,
|
||||
publishing_yaml TEXT NOT NULL,
|
||||
publishing_yaml_hash TEXT NOT NULL,
|
||||
transform_script TEXT NOT NULL,
|
||||
transform_script_hash TEXT NOT NULL,
|
||||
UNIQUE (target_site_id, version)
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS articles (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
target_site_id UUID NOT NULL REFERENCES target_sites(id),
|
||||
status TEXT NOT NULL CHECK (status IN ({ARTICLE_STATUS_VALUES})),
|
||||
publishing_status TEXT NOT NULL CHECK (
|
||||
publishing_status IN ({PUBLISHING_STATUS_VALUES})
|
||||
),
|
||||
brief_description TEXT NOT NULL,
|
||||
working_title TEXT,
|
||||
language TEXT NOT NULL DEFAULT 'en',
|
||||
content_type TEXT NOT NULL DEFAULT 'longform_guide',
|
||||
primary_keyword TEXT,
|
||||
assigned_editor_id UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS boundary_questions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
sort_order INTEGER NOT NULL,
|
||||
question TEXT NOT NULL,
|
||||
answer TEXT,
|
||||
is_required BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (article_id, sort_order)
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS article_plans (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ({PLAN_REVIEW_STATUS_VALUES})),
|
||||
recommended_title TEXT,
|
||||
search_intent TEXT,
|
||||
thesis TEXT,
|
||||
claims_to_prove JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
risks JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (article_id, version)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS plan_sections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_plan_id UUID NOT NULL REFERENCES article_plans(id) ON DELETE CASCADE,
|
||||
sort_order INTEGER NOT NULL,
|
||||
heading TEXT NOT NULL,
|
||||
purpose TEXT,
|
||||
claims_to_support JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
target_word_count INTEGER,
|
||||
UNIQUE (article_plan_id, sort_order)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS evidence_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
source_title TEXT NOT NULL,
|
||||
source_url TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL,
|
||||
source_quality_score NUMERIC(4, 3) NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
supports_claims JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
artifact_manifest_id UUID,
|
||||
retrieved_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS claims (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
section_id UUID REFERENCES plan_sections(id) ON DELETE SET NULL,
|
||||
claim_text TEXT NOT NULL,
|
||||
support_status TEXT NOT NULL CHECK (
|
||||
support_status IN ({CLAIM_SUPPORT_STATUS_VALUES})
|
||||
),
|
||||
risk_level TEXT NOT NULL CHECK (risk_level IN ({CLAIM_RISK_LEVEL_VALUES})),
|
||||
evidence_item_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS article_drafts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
meta_title TEXT,
|
||||
meta_description TEXT,
|
||||
body_object_key TEXT,
|
||||
status TEXT NOT NULL CHECK (status IN ({ARTICLE_STATUS_VALUES})),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (article_id, version)
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS assets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
asset_type TEXT NOT NULL CHECK (asset_type IN ({ASSET_TYPE_VALUES})),
|
||||
title TEXT NOT NULL,
|
||||
prompt TEXT,
|
||||
file_url TEXT,
|
||||
alt_text TEXT,
|
||||
caption TEXT,
|
||||
status TEXT NOT NULL CHECK (status IN ({ASSET_STATUS_VALUES})),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS workflow_events (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
event_type TEXT NOT NULL,
|
||||
from_status TEXT CHECK (from_status IN ({ARTICLE_STATUS_VALUES})),
|
||||
to_status TEXT CHECK (to_status IN ({ARTICLE_STATUS_VALUES})),
|
||||
actor_user_id UUID REFERENCES users(id),
|
||||
payload JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS agent_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID REFERENCES articles(id) ON DELETE SET NULL,
|
||||
job_type TEXT NOT NULL CHECK (job_type IN ({AGENT_JOB_TYPE_VALUES})),
|
||||
agent_profile TEXT NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ({AGENT_JOB_STATUS_VALUES})),
|
||||
workspace_path TEXT,
|
||||
input_files JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
output_files JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
error_category TEXT CHECK (
|
||||
error_category IS NULL
|
||||
OR error_category IN ({AGENT_JOB_ERROR_CATEGORY_VALUES})
|
||||
),
|
||||
error_message TEXT,
|
||||
queued_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
started_at TIMESTAMPTZ,
|
||||
finished_at TIMESTAMPTZ
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS research_run_manifests (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
agent_job_id UUID NOT NULL REFERENCES agent_jobs(id) ON DELETE CASCADE,
|
||||
s3_prefix TEXT NOT NULL,
|
||||
source_urls JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
object_keys JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
content_hashes JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
artifact_types JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
metadata_references JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
artifacts JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS publish_commits (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
article_id UUID NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
target_site_id UUID NOT NULL REFERENCES target_sites(id),
|
||||
repository_url TEXT NOT NULL,
|
||||
branch TEXT NOT NULL,
|
||||
commit_sha TEXT,
|
||||
content_bundle_manifest JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
||||
status TEXT NOT NULL CHECK (status IN ({PUBLISHING_STATUS_VALUES})),
|
||||
deployment_status TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)
|
||||
""",
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS prompt_versions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
prompt_key TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ({SCRIPT_CONFIG_STATUS_VALUES})),
|
||||
body TEXT NOT NULL,
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
activated_at TIMESTAMPTZ,
|
||||
diff JSONB NOT NULL DEFAULT '{{}}'::jsonb,
|
||||
UNIQUE (prompt_key, version)
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_target_sites_slug ON target_sites (slug)",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_dashboard
|
||||
ON articles (target_site_id, status, publishing_status, updated_at DESC)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_editor_dashboard
|
||||
ON articles (assigned_editor_id, updated_at DESC)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_jobs_queue
|
||||
ON agent_jobs (status, queued_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_jobs_article_timeline
|
||||
ON agent_jobs (article_id, job_type, queued_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_workflow_events_timeline
|
||||
ON workflow_events (article_id, created_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_research_run_manifests_article
|
||||
ON research_run_manifests (article_id, created_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_publish_commits_article
|
||||
ON publish_commits (article_id, created_at)
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
SQLITE_SCHEMA_STATEMENTS: tuple[str, ...] = (
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT PRIMARY KEY,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS target_sites (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
publishing_type TEXT NOT NULL DEFAULT 'git_next',
|
||||
default_language TEXT NOT NULL DEFAULT 'en',
|
||||
brand_voice TEXT NOT NULL,
|
||||
audience TEXT NOT NULL,
|
||||
seo_rules TEXT NOT NULL DEFAULT '{}',
|
||||
visual_rules TEXT NOT NULL DEFAULT '{}',
|
||||
source_rules TEXT NOT NULL DEFAULT '{}',
|
||||
publishing_rules TEXT NOT NULL DEFAULT '{}',
|
||||
active_script_config_version_id TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS script_config_versions (
|
||||
id TEXT PRIMARY KEY,
|
||||
target_site_id TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
diff TEXT NOT NULL DEFAULT '{}',
|
||||
rollback_target_version_id TEXT,
|
||||
activated_at TEXT,
|
||||
publishing_yaml TEXT NOT NULL,
|
||||
publishing_yaml_hash TEXT NOT NULL,
|
||||
transform_script TEXT NOT NULL,
|
||||
transform_script_hash TEXT NOT NULL,
|
||||
UNIQUE (target_site_id, version)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS articles (
|
||||
id TEXT PRIMARY KEY,
|
||||
target_site_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
publishing_status TEXT NOT NULL,
|
||||
brief_description TEXT NOT NULL,
|
||||
working_title TEXT,
|
||||
language TEXT NOT NULL DEFAULT 'en',
|
||||
content_type TEXT NOT NULL DEFAULT 'longform_guide',
|
||||
primary_keyword TEXT,
|
||||
assigned_editor_id TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS boundary_questions (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
sort_order INTEGER NOT NULL,
|
||||
question TEXT NOT NULL,
|
||||
answer TEXT,
|
||||
is_required INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (article_id, sort_order)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS article_plans (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
recommended_title TEXT,
|
||||
search_intent TEXT,
|
||||
thesis TEXT,
|
||||
claims_to_prove TEXT NOT NULL DEFAULT '[]',
|
||||
risks TEXT NOT NULL DEFAULT '[]',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (article_id, version)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS plan_sections (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_plan_id TEXT NOT NULL,
|
||||
sort_order INTEGER NOT NULL,
|
||||
heading TEXT NOT NULL,
|
||||
purpose TEXT,
|
||||
claims_to_support TEXT NOT NULL DEFAULT '[]',
|
||||
target_word_count INTEGER,
|
||||
UNIQUE (article_plan_id, sort_order)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS evidence_items (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
source_title TEXT NOT NULL,
|
||||
source_url TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL,
|
||||
source_quality_score REAL NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
supports_claims TEXT NOT NULL DEFAULT '[]',
|
||||
artifact_manifest_id TEXT,
|
||||
retrieved_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS claims (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
section_id TEXT,
|
||||
claim_text TEXT NOT NULL,
|
||||
support_status TEXT NOT NULL,
|
||||
risk_level TEXT NOT NULL,
|
||||
evidence_item_ids TEXT NOT NULL DEFAULT '[]',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS article_drafts (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
meta_title TEXT,
|
||||
meta_description TEXT,
|
||||
body_object_key TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (article_id, version)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS assets (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
asset_type TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
prompt TEXT,
|
||||
file_url TEXT,
|
||||
alt_text TEXT,
|
||||
caption TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS workflow_events (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
from_status TEXT,
|
||||
to_status TEXT,
|
||||
actor_user_id TEXT,
|
||||
payload TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS agent_jobs (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT,
|
||||
job_type TEXT NOT NULL,
|
||||
agent_profile TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
workspace_path TEXT,
|
||||
input_files TEXT NOT NULL DEFAULT '[]',
|
||||
output_files TEXT NOT NULL DEFAULT '[]',
|
||||
error_category TEXT,
|
||||
error_message TEXT,
|
||||
queued_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at TEXT,
|
||||
finished_at TEXT
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS research_run_manifests (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
agent_job_id TEXT NOT NULL,
|
||||
s3_prefix TEXT NOT NULL,
|
||||
source_urls TEXT NOT NULL DEFAULT '[]',
|
||||
object_keys TEXT NOT NULL DEFAULT '[]',
|
||||
content_hashes TEXT NOT NULL DEFAULT '[]',
|
||||
artifact_types TEXT NOT NULL DEFAULT '[]',
|
||||
metadata_references TEXT NOT NULL DEFAULT '[]',
|
||||
artifacts TEXT NOT NULL DEFAULT '[]',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS publish_commits (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL,
|
||||
target_site_id TEXT NOT NULL,
|
||||
repository_url TEXT NOT NULL,
|
||||
branch TEXT NOT NULL,
|
||||
commit_sha TEXT,
|
||||
content_bundle_manifest TEXT NOT NULL DEFAULT '{}',
|
||||
status TEXT NOT NULL,
|
||||
deployment_status TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS prompt_versions (
|
||||
id TEXT PRIMARY KEY,
|
||||
prompt_key TEXT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
activated_at TEXT,
|
||||
diff TEXT NOT NULL DEFAULT '{}',
|
||||
UNIQUE (prompt_key, version)
|
||||
)
|
||||
""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_target_sites_slug ON target_sites (slug)",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_dashboard
|
||||
ON articles (target_site_id, status, publishing_status, updated_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_editor_dashboard
|
||||
ON articles (assigned_editor_id, updated_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_jobs_queue
|
||||
ON agent_jobs (status, queued_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_jobs_article_timeline
|
||||
ON agent_jobs (article_id, job_type, queued_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_workflow_events_timeline
|
||||
ON workflow_events (article_id, created_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_research_run_manifests_article
|
||||
ON research_run_manifests (article_id, created_at)
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_publish_commits_article
|
||||
ON publish_commits (article_id, created_at)
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def schema_statements_for_dsn(dsn: str) -> tuple[str, ...]:
|
||||
if dsn.startswith("sqlite:///"):
|
||||
return SQLITE_SCHEMA_STATEMENTS
|
||||
return POSTGRES_SCHEMA_STATEMENTS
|
||||
|
||||
|
||||
def setup_database(dsn: str) -> None:
|
||||
statements = schema_statements_for_dsn(dsn)
|
||||
if dsn.startswith("sqlite:///"):
|
||||
sqlite_path = dsn.removeprefix("sqlite:///")
|
||||
with closing(sqlite3.connect(sqlite_path)) as connection:
|
||||
for statement in statements:
|
||||
connection.execute(statement)
|
||||
connection.commit()
|
||||
return
|
||||
|
||||
import psycopg
|
||||
|
||||
with psycopg.connect(dsn) as connection:
|
||||
with connection.cursor() as cursor:
|
||||
for statement in statements:
|
||||
cursor.execute(statement)
|
||||
Reference in New Issue
Block a user