From dde49442d0bab76a05ceaf4a37bc84e34ee8456f Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Wed, 11 Mar 2026 02:06:01 +0100 Subject: [PATCH 1/3] feat: Convert VARCHAR enum columns to native PG enums with values_callable Three enum columns (region_type, report_type, decile_type) were stored as VARCHAR but needed native PG enum types for proper SQLAlchemy deserialization. Adds values_callable to store lowercase values and includes Alembic migration that drops pre-existing uppercase enum types before recreating with lowercase. Co-Authored-By: Claude Opus 4.6 --- ...8049d_rename_tax_benefit_model_name_to_.py | 69 ++++++++++++++ ..._add_execution_deferred_to_reportstatus.py | 31 +++++++ ...onvert_varchar_enums_to_native_pg_enums.py | 90 +++++++++++++++++++ .../models/intra_decile_impact.py | 6 +- src/policyengine_api/models/region.py | 5 +- src/policyengine_api/models/report.py | 6 +- 6 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py create mode 100644 alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py create mode 100644 alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py diff --git a/alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py b/alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py new file mode 100644 index 0000000..5923b22 --- /dev/null +++ b/alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py @@ -0,0 +1,69 @@ +"""rename_tax_benefit_model_name_to_country_id + +Revision ID: 62385cd8049d +Revises: 886921687770 +Create Date: 2026-03-09 16:48:30.899791 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes + + +# revision identifiers, used by Alembic. +revision: str = '62385cd8049d' +down_revision: Union[str, Sequence[str], None] = '886921687770' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema: rename tax_benefit_model_name → country_id with data migration.""" + # 1. Add country_id columns (nullable initially) + op.add_column('households', sa.Column('country_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) + op.add_column('household_jobs', sa.Column('country_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) + + # 2. Populate country_id from tax_benefit_model_name + op.execute(""" + UPDATE households SET country_id = CASE + WHEN tax_benefit_model_name LIKE '%_us' OR tax_benefit_model_name LIKE '%-us' THEN 'us' + WHEN tax_benefit_model_name LIKE '%_uk' OR tax_benefit_model_name LIKE '%-uk' THEN 'uk' + ELSE 'us' + END + """) + op.execute(""" + UPDATE household_jobs SET country_id = CASE + WHEN tax_benefit_model_name LIKE '%_us' OR tax_benefit_model_name LIKE '%-us' THEN 'us' + WHEN tax_benefit_model_name LIKE '%_uk' OR tax_benefit_model_name LIKE '%-uk' THEN 'uk' + ELSE 'us' + END + """) + + # 3. Make country_id non-nullable + op.alter_column('households', 'country_id', nullable=False) + op.alter_column('household_jobs', 'country_id', nullable=False) + + # 4. Drop old columns + op.drop_column('households', 'tax_benefit_model_name') + op.drop_column('household_jobs', 'tax_benefit_model_name') + + +def downgrade() -> None: + """Downgrade schema: restore tax_benefit_model_name from country_id.""" + # 1. Re-add tax_benefit_model_name columns (nullable initially) + op.add_column('households', sa.Column('tax_benefit_model_name', sa.VARCHAR(), nullable=True)) + op.add_column('household_jobs', sa.Column('tax_benefit_model_name', sa.VARCHAR(), nullable=True)) + + # 2. Populate from country_id + op.execute("UPDATE households SET tax_benefit_model_name = 'policyengine_' || country_id") + op.execute("UPDATE household_jobs SET tax_benefit_model_name = 'policyengine_' || country_id") + + # 3. Make non-nullable + op.alter_column('households', 'tax_benefit_model_name', nullable=False) + op.alter_column('household_jobs', 'tax_benefit_model_name', nullable=False) + + # 4. Drop country_id columns + op.drop_column('households', 'country_id') + op.drop_column('household_jobs', 'country_id') diff --git a/alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py b/alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py new file mode 100644 index 0000000..25440d2 --- /dev/null +++ b/alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py @@ -0,0 +1,31 @@ +"""add_execution_deferred_to_reportstatus + +Revision ID: f887cb5490bc +Revises: 62385cd8049d +Create Date: 2026-03-10 21:27:32.072364 + +""" +from typing import Sequence, Union + +from alembic import op + + +# revision identifiers, used by Alembic. +revision: str = 'f887cb5490bc' +down_revision: Union[str, Sequence[str], None] = '62385cd8049d' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Add EXECUTION_DEFERRED value to the reportstatus enum.""" + op.execute("ALTER TYPE reportstatus ADD VALUE IF NOT EXISTS 'EXECUTION_DEFERRED'") + + +def downgrade() -> None: + """Downgrade: PostgreSQL does not support removing enum values. + + The 'EXECUTION_DEFERRED' value will remain in the enum type. + To fully remove it, drop and recreate the type (requires migrating data). + """ + pass diff --git a/alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py b/alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py new file mode 100644 index 0000000..3307f43 --- /dev/null +++ b/alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py @@ -0,0 +1,90 @@ +"""convert_varchar_enums_to_native_pg_enums + +Revision ID: dac22a838dda +Revises: f887cb5490bc +Create Date: 2026-03-11 01:37:08.928795 + +""" +from typing import Sequence, Union + +from alembic import op + + +# revision identifiers, used by Alembic. +revision: str = 'dac22a838dda' +down_revision: Union[str, Sequence[str], None] = 'f887cb5490bc' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Convert VARCHAR enum columns to native PostgreSQL enum types. + + The enum types may already exist with UPPERCASE values (created by + SQLAlchemy's default create_all behavior). Since the columns are still + VARCHAR, the types are unused — drop and recreate with lowercase values + matching the data and the values_callable convention. + """ + # Drop any pre-existing enum types (unused — columns are still VARCHAR) + op.execute("DROP TYPE IF EXISTS regiontype CASCADE") + op.execute("DROP TYPE IF EXISTS reporttype CASCADE") + op.execute("DROP TYPE IF EXISTS deciletype CASCADE") + + # Create PG enum types with lowercase values + op.execute(""" + CREATE TYPE regiontype AS ENUM ( + 'national', 'country', 'state', 'congressional_district', + 'constituency', 'local_authority', 'city', 'place' + ) + """) + op.execute(""" + CREATE TYPE reporttype AS ENUM ( + 'economy_comparison', 'household_comparison', 'household_single' + ) + """) + op.execute("CREATE TYPE deciletype AS ENUM ('income', 'wealth')") + + # Alter columns from VARCHAR to enum. + # LOWER() handles any databases where values were previously uppercased. + op.execute(""" + ALTER TABLE regions + ALTER COLUMN region_type TYPE regiontype + USING LOWER(region_type)::regiontype + """) + op.execute(""" + ALTER TABLE reports + ALTER COLUMN report_type TYPE reporttype + USING LOWER(report_type)::reporttype + """) + # decile_type has a VARCHAR default that must be dropped before type change + op.execute("ALTER TABLE intra_decile_impacts ALTER COLUMN decile_type DROP DEFAULT") + op.execute(""" + ALTER TABLE intra_decile_impacts + ALTER COLUMN decile_type TYPE deciletype + USING LOWER(decile_type)::deciletype + """) + op.execute("ALTER TABLE intra_decile_impacts ALTER COLUMN decile_type SET DEFAULT 'income'::deciletype") + + +def downgrade() -> None: + """Revert native PG enum columns back to VARCHAR.""" + op.execute(""" + ALTER TABLE regions + ALTER COLUMN region_type TYPE VARCHAR + USING region_type::text + """) + op.execute(""" + ALTER TABLE reports + ALTER COLUMN report_type TYPE VARCHAR + USING report_type::text + """) + op.execute(""" + ALTER TABLE intra_decile_impacts + ALTER COLUMN decile_type TYPE VARCHAR + USING decile_type::text + """) + + # Drop the PG enum types + op.execute("DROP TYPE IF EXISTS regiontype") + op.execute("DROP TYPE IF EXISTS reporttype") + op.execute("DROP TYPE IF EXISTS deciletype") diff --git a/src/policyengine_api/models/intra_decile_impact.py b/src/policyengine_api/models/intra_decile_impact.py index 8771d55..a8c958a 100644 --- a/src/policyengine_api/models/intra_decile_impact.py +++ b/src/policyengine_api/models/intra_decile_impact.py @@ -19,6 +19,7 @@ from enum import Enum from uuid import UUID, uuid4 +import sqlalchemy as sa from sqlmodel import Field, SQLModel @@ -35,7 +36,10 @@ class IntraDecileImpactBase(SQLModel): baseline_simulation_id: UUID = Field(foreign_key="simulations.id") reform_simulation_id: UUID = Field(foreign_key="simulations.id") report_id: UUID | None = Field(default=None, foreign_key="reports.id") - decile_type: DecileType = Field(default=DecileType.INCOME) + decile_type: DecileType = Field( + default=DecileType.INCOME, + sa_type=sa.Enum(DecileType, values_callable=lambda x: [e.value for e in x]), + ) decile: int = Field(ge=0, le=10) lose_more_than_5pct: float | None = Field(default=None, ge=0.0, le=1.0) lose_less_than_5pct: float | None = Field(default=None, ge=0.0, le=1.0) diff --git a/src/policyengine_api/models/region.py b/src/policyengine_api/models/region.py index 29c2785..ee42360 100644 --- a/src/policyengine_api/models/region.py +++ b/src/policyengine_api/models/region.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING from uuid import UUID, uuid4 +import sqlalchemy as sa from pydantic import model_validator from sqlmodel import Field, Relationship, SQLModel @@ -33,7 +34,9 @@ class RegionBase(SQLModel): code: str # e.g., "state/ca", "constituency/Sheffield Central" label: str # e.g., "California", "Sheffield Central" - region_type: RegionType # e.g., RegionType.STATE, RegionType.CONSTITUENCY + region_type: RegionType = Field( + sa_type=sa.Enum(RegionType, values_callable=lambda x: [e.value for e in x]), + ) requires_filter: bool = False filter_field: str | None = None # e.g., "state_code", "place_fips" filter_value: str | None = None # e.g., "CA", "44000" diff --git a/src/policyengine_api/models/report.py b/src/policyengine_api/models/report.py index b034dcb..60e6c3a 100644 --- a/src/policyengine_api/models/report.py +++ b/src/policyengine_api/models/report.py @@ -2,6 +2,7 @@ from enum import Enum from uuid import UUID, uuid4 +import sqlalchemy as sa from sqlmodel import Column, Field, SQLModel, Text @@ -27,7 +28,10 @@ class ReportBase(SQLModel): label: str description: str | None = None - report_type: ReportType | None = None + report_type: ReportType | None = Field( + default=None, + sa_type=sa.Enum(ReportType, values_callable=lambda x: [e.value for e in x], nullable=True), + ) user_id: UUID | None = Field(default=None, foreign_key="users.id") markdown: str | None = Field(default=None, sa_column=Column(Text)) status: ReportStatus = ReportStatus.PENDING From a0d1fccd22d96861cd32f15e9a91448dba01c411 Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Wed, 11 Mar 2026 18:25:05 +0100 Subject: [PATCH 2/3] fix: Fix staging/canary URL extraction in deploy workflow The gcloud format string `value(status.traffic[tag=staging].url)` silently returns empty, causing the health check to curl just "/health" with no hostname (HTTP 000). Replaced with JSON output piped through jq for reliable tagged URL extraction. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/deploy.yml | 4 ++-- changelog.d/fix-tagged-url-extraction.fixed | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog.d/fix-tagged-url-extraction.fixed diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b912e08..6404c89 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -263,7 +263,7 @@ jobs: run: | STAGING_URL=$(gcloud run services describe ${{ vars.API_SERVICE_NAME }} \ --region=${{ vars.GCP_REGION }} \ - --format='value(status.traffic[tag=staging].url)') + --format=json | jq -r '.status.traffic[] | select(.tag=="staging") | .url') echo "url=$STAGING_URL" >> "$GITHUB_OUTPUT" echo "Staging URL: $STAGING_URL" @@ -403,7 +403,7 @@ jobs: run: | CANARY_URL=$(gcloud run services describe ${{ vars.API_SERVICE_NAME }} \ --region=${{ vars.GCP_REGION }} \ - --format='value(status.traffic[tag=canary].url)') + --format=json | jq -r '.status.traffic[] | select(.tag=="canary") | .url') echo "Canary URL: $CANARY_URL" chmod +x .github/scripts/*.sh .github/scripts/health-check.sh "$CANARY_URL/health" diff --git a/changelog.d/fix-tagged-url-extraction.fixed b/changelog.d/fix-tagged-url-extraction.fixed new file mode 100644 index 0000000..28861cf --- /dev/null +++ b/changelog.d/fix-tagged-url-extraction.fixed @@ -0,0 +1 @@ +Fix staging and canary URL extraction in deploy workflow — gcloud format filter returned empty, replaced with JSON + jq parsing. From b79e80beb719a9127ff6e290e1365271c8845270 Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Wed, 11 Mar 2026 18:38:09 +0100 Subject: [PATCH 3/3] Revert "feat: Convert VARCHAR enum columns to native PG enums with values_callable" This reverts commit dde49442d0bab76a05ceaf4a37bc84e34ee8456f. --- ...8049d_rename_tax_benefit_model_name_to_.py | 69 -------------- ..._add_execution_deferred_to_reportstatus.py | 31 ------- ...onvert_varchar_enums_to_native_pg_enums.py | 90 ------------------- .../models/intra_decile_impact.py | 6 +- src/policyengine_api/models/region.py | 5 +- src/policyengine_api/models/report.py | 6 +- 6 files changed, 3 insertions(+), 204 deletions(-) delete mode 100644 alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py delete mode 100644 alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py delete mode 100644 alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py diff --git a/alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py b/alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py deleted file mode 100644 index 5923b22..0000000 --- a/alembic/versions/20260309_62385cd8049d_rename_tax_benefit_model_name_to_.py +++ /dev/null @@ -1,69 +0,0 @@ -"""rename_tax_benefit_model_name_to_country_id - -Revision ID: 62385cd8049d -Revises: 886921687770 -Create Date: 2026-03-09 16:48:30.899791 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import sqlmodel.sql.sqltypes - - -# revision identifiers, used by Alembic. -revision: str = '62385cd8049d' -down_revision: Union[str, Sequence[str], None] = '886921687770' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema: rename tax_benefit_model_name → country_id with data migration.""" - # 1. Add country_id columns (nullable initially) - op.add_column('households', sa.Column('country_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) - op.add_column('household_jobs', sa.Column('country_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) - - # 2. Populate country_id from tax_benefit_model_name - op.execute(""" - UPDATE households SET country_id = CASE - WHEN tax_benefit_model_name LIKE '%_us' OR tax_benefit_model_name LIKE '%-us' THEN 'us' - WHEN tax_benefit_model_name LIKE '%_uk' OR tax_benefit_model_name LIKE '%-uk' THEN 'uk' - ELSE 'us' - END - """) - op.execute(""" - UPDATE household_jobs SET country_id = CASE - WHEN tax_benefit_model_name LIKE '%_us' OR tax_benefit_model_name LIKE '%-us' THEN 'us' - WHEN tax_benefit_model_name LIKE '%_uk' OR tax_benefit_model_name LIKE '%-uk' THEN 'uk' - ELSE 'us' - END - """) - - # 3. Make country_id non-nullable - op.alter_column('households', 'country_id', nullable=False) - op.alter_column('household_jobs', 'country_id', nullable=False) - - # 4. Drop old columns - op.drop_column('households', 'tax_benefit_model_name') - op.drop_column('household_jobs', 'tax_benefit_model_name') - - -def downgrade() -> None: - """Downgrade schema: restore tax_benefit_model_name from country_id.""" - # 1. Re-add tax_benefit_model_name columns (nullable initially) - op.add_column('households', sa.Column('tax_benefit_model_name', sa.VARCHAR(), nullable=True)) - op.add_column('household_jobs', sa.Column('tax_benefit_model_name', sa.VARCHAR(), nullable=True)) - - # 2. Populate from country_id - op.execute("UPDATE households SET tax_benefit_model_name = 'policyengine_' || country_id") - op.execute("UPDATE household_jobs SET tax_benefit_model_name = 'policyengine_' || country_id") - - # 3. Make non-nullable - op.alter_column('households', 'tax_benefit_model_name', nullable=False) - op.alter_column('household_jobs', 'tax_benefit_model_name', nullable=False) - - # 4. Drop country_id columns - op.drop_column('households', 'country_id') - op.drop_column('household_jobs', 'country_id') diff --git a/alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py b/alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py deleted file mode 100644 index 25440d2..0000000 --- a/alembic/versions/20260310_f887cb5490bc_add_execution_deferred_to_reportstatus.py +++ /dev/null @@ -1,31 +0,0 @@ -"""add_execution_deferred_to_reportstatus - -Revision ID: f887cb5490bc -Revises: 62385cd8049d -Create Date: 2026-03-10 21:27:32.072364 - -""" -from typing import Sequence, Union - -from alembic import op - - -# revision identifiers, used by Alembic. -revision: str = 'f887cb5490bc' -down_revision: Union[str, Sequence[str], None] = '62385cd8049d' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Add EXECUTION_DEFERRED value to the reportstatus enum.""" - op.execute("ALTER TYPE reportstatus ADD VALUE IF NOT EXISTS 'EXECUTION_DEFERRED'") - - -def downgrade() -> None: - """Downgrade: PostgreSQL does not support removing enum values. - - The 'EXECUTION_DEFERRED' value will remain in the enum type. - To fully remove it, drop and recreate the type (requires migrating data). - """ - pass diff --git a/alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py b/alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py deleted file mode 100644 index 3307f43..0000000 --- a/alembic/versions/20260311_dac22a838dda_convert_varchar_enums_to_native_pg_enums.py +++ /dev/null @@ -1,90 +0,0 @@ -"""convert_varchar_enums_to_native_pg_enums - -Revision ID: dac22a838dda -Revises: f887cb5490bc -Create Date: 2026-03-11 01:37:08.928795 - -""" -from typing import Sequence, Union - -from alembic import op - - -# revision identifiers, used by Alembic. -revision: str = 'dac22a838dda' -down_revision: Union[str, Sequence[str], None] = 'f887cb5490bc' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Convert VARCHAR enum columns to native PostgreSQL enum types. - - The enum types may already exist with UPPERCASE values (created by - SQLAlchemy's default create_all behavior). Since the columns are still - VARCHAR, the types are unused — drop and recreate with lowercase values - matching the data and the values_callable convention. - """ - # Drop any pre-existing enum types (unused — columns are still VARCHAR) - op.execute("DROP TYPE IF EXISTS regiontype CASCADE") - op.execute("DROP TYPE IF EXISTS reporttype CASCADE") - op.execute("DROP TYPE IF EXISTS deciletype CASCADE") - - # Create PG enum types with lowercase values - op.execute(""" - CREATE TYPE regiontype AS ENUM ( - 'national', 'country', 'state', 'congressional_district', - 'constituency', 'local_authority', 'city', 'place' - ) - """) - op.execute(""" - CREATE TYPE reporttype AS ENUM ( - 'economy_comparison', 'household_comparison', 'household_single' - ) - """) - op.execute("CREATE TYPE deciletype AS ENUM ('income', 'wealth')") - - # Alter columns from VARCHAR to enum. - # LOWER() handles any databases where values were previously uppercased. - op.execute(""" - ALTER TABLE regions - ALTER COLUMN region_type TYPE regiontype - USING LOWER(region_type)::regiontype - """) - op.execute(""" - ALTER TABLE reports - ALTER COLUMN report_type TYPE reporttype - USING LOWER(report_type)::reporttype - """) - # decile_type has a VARCHAR default that must be dropped before type change - op.execute("ALTER TABLE intra_decile_impacts ALTER COLUMN decile_type DROP DEFAULT") - op.execute(""" - ALTER TABLE intra_decile_impacts - ALTER COLUMN decile_type TYPE deciletype - USING LOWER(decile_type)::deciletype - """) - op.execute("ALTER TABLE intra_decile_impacts ALTER COLUMN decile_type SET DEFAULT 'income'::deciletype") - - -def downgrade() -> None: - """Revert native PG enum columns back to VARCHAR.""" - op.execute(""" - ALTER TABLE regions - ALTER COLUMN region_type TYPE VARCHAR - USING region_type::text - """) - op.execute(""" - ALTER TABLE reports - ALTER COLUMN report_type TYPE VARCHAR - USING report_type::text - """) - op.execute(""" - ALTER TABLE intra_decile_impacts - ALTER COLUMN decile_type TYPE VARCHAR - USING decile_type::text - """) - - # Drop the PG enum types - op.execute("DROP TYPE IF EXISTS regiontype") - op.execute("DROP TYPE IF EXISTS reporttype") - op.execute("DROP TYPE IF EXISTS deciletype") diff --git a/src/policyengine_api/models/intra_decile_impact.py b/src/policyengine_api/models/intra_decile_impact.py index a8c958a..8771d55 100644 --- a/src/policyengine_api/models/intra_decile_impact.py +++ b/src/policyengine_api/models/intra_decile_impact.py @@ -19,7 +19,6 @@ from enum import Enum from uuid import UUID, uuid4 -import sqlalchemy as sa from sqlmodel import Field, SQLModel @@ -36,10 +35,7 @@ class IntraDecileImpactBase(SQLModel): baseline_simulation_id: UUID = Field(foreign_key="simulations.id") reform_simulation_id: UUID = Field(foreign_key="simulations.id") report_id: UUID | None = Field(default=None, foreign_key="reports.id") - decile_type: DecileType = Field( - default=DecileType.INCOME, - sa_type=sa.Enum(DecileType, values_callable=lambda x: [e.value for e in x]), - ) + decile_type: DecileType = Field(default=DecileType.INCOME) decile: int = Field(ge=0, le=10) lose_more_than_5pct: float | None = Field(default=None, ge=0.0, le=1.0) lose_less_than_5pct: float | None = Field(default=None, ge=0.0, le=1.0) diff --git a/src/policyengine_api/models/region.py b/src/policyengine_api/models/region.py index ee42360..29c2785 100644 --- a/src/policyengine_api/models/region.py +++ b/src/policyengine_api/models/region.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING from uuid import UUID, uuid4 -import sqlalchemy as sa from pydantic import model_validator from sqlmodel import Field, Relationship, SQLModel @@ -34,9 +33,7 @@ class RegionBase(SQLModel): code: str # e.g., "state/ca", "constituency/Sheffield Central" label: str # e.g., "California", "Sheffield Central" - region_type: RegionType = Field( - sa_type=sa.Enum(RegionType, values_callable=lambda x: [e.value for e in x]), - ) + region_type: RegionType # e.g., RegionType.STATE, RegionType.CONSTITUENCY requires_filter: bool = False filter_field: str | None = None # e.g., "state_code", "place_fips" filter_value: str | None = None # e.g., "CA", "44000" diff --git a/src/policyengine_api/models/report.py b/src/policyengine_api/models/report.py index 60e6c3a..b034dcb 100644 --- a/src/policyengine_api/models/report.py +++ b/src/policyengine_api/models/report.py @@ -2,7 +2,6 @@ from enum import Enum from uuid import UUID, uuid4 -import sqlalchemy as sa from sqlmodel import Column, Field, SQLModel, Text @@ -28,10 +27,7 @@ class ReportBase(SQLModel): label: str description: str | None = None - report_type: ReportType | None = Field( - default=None, - sa_type=sa.Enum(ReportType, values_callable=lambda x: [e.value for e in x], nullable=True), - ) + report_type: ReportType | None = None user_id: UUID | None = Field(default=None, foreign_key="users.id") markdown: str | None = Field(default=None, sa_column=Column(Text)) status: ReportStatus = ReportStatus.PENDING