-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into population-bar-bugfixes
- Loading branch information
Showing
22 changed files
with
592 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
backend/app/alembic/versions/552ac8c1defd_export_zone_assignments.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
"""export zone assignments | ||
Revision ID: 552ac8c1defd | ||
Revises: 0f8bbbcdd7be | ||
Create Date: 2025-01-19 19:51:42.117991 | ||
""" | ||
|
||
from typing import Sequence, Union | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
from app.main import settings | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision: str = "552ac8c1defd" | ||
down_revision: Union[str, None] = "c41fbcfff93e" | ||
branch_labels: Union[str, Sequence[str], None] = None | ||
depends_on: Union[str, Sequence[str], None] = None | ||
|
||
sql_files = [ | ||
"export_zone_assignments_geo.sql", | ||
"get_block_assignments.sql", | ||
"get_block_assignments_geo.sql", | ||
] | ||
|
||
|
||
def upgrade() -> None: | ||
for file in sql_files: | ||
with open(settings.SQL_DIR / file) as f: | ||
stmt = f.read() | ||
op.execute(sa.text(stmt)) | ||
|
||
|
||
def downgrade() -> None: | ||
op.execute(sa.text("DROP FUNCTION IF EXISTS get_zone_assignments_geo(UUID)")) | ||
op.execute(sa.text("DROP FUNCTION IF EXISTS get_block_assignments(UUID)")) | ||
op.execute(sa.text("DROP FUNCTION IF EXISTS get_block_assignments_geo(UUID)")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from app.exports.main import get_export_sql_method | ||
from app.exports.models import DocumentExportFormat, DocumentExportType | ||
|
||
__all__ = [ | ||
"get_export_sql_method", | ||
"DocumentExportFormat", | ||
"DocumentExportType", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from psycopg.sql import SQL, Composed, Identifier, Literal | ||
from typing import Callable, Any | ||
from app.exports.models import ( | ||
DocumentExportFormat, | ||
DocumentExportType, | ||
) | ||
|
||
|
||
def get_export_sql_method( | ||
format: DocumentExportFormat, | ||
) -> Callable[..., tuple[Composed, list[Any]]]: | ||
if format == DocumentExportFormat.geojson: | ||
return get_geojson_export_sql | ||
elif format == DocumentExportFormat.csv: | ||
return get_csv_export_sql | ||
|
||
raise NotImplementedError(f"'{format}' export format is not yet supported") | ||
|
||
|
||
def get_csv_export_sql( | ||
export_type: DocumentExportType, **kwargs | ||
) -> tuple[Composed, list[Any]]: | ||
stmt = None | ||
params = [] | ||
|
||
if export_type == DocumentExportType.zone_assignments: | ||
stmt = """SELECT | ||
geo_id, | ||
zone::TEXT AS zone | ||
FROM document.assignments | ||
WHERE document_id = %s | ||
ORDER BY geo_id""" | ||
params += [kwargs["document_id"]] | ||
|
||
elif export_type == DocumentExportType.block_zone_assignments: | ||
stmt = """SELECT * FROM get_block_assignments(%s)""" | ||
params += [kwargs["document_id"]] | ||
|
||
if stmt is None: | ||
raise NotImplementedError("Document export type is not yet supported") | ||
|
||
sql = SQL("COPY ( {} ) TO STDOUT WITH (FORMAT CSV, HEADER, DELIMITER ',')").format( | ||
SQL(stmt) # pyright: ignore | ||
) | ||
|
||
return sql, params | ||
|
||
|
||
def get_geojson_export_sql( | ||
export_type: DocumentExportType, **kwargs | ||
) -> tuple[Composed, list[Any]]: | ||
stmt, geom_type, _id = None, None, None | ||
params = [] | ||
|
||
if export_type == DocumentExportType.zone_assignments: | ||
stmt = "SELECT * FROM get_zone_assignments_geo(%s::UUID)" | ||
params += [kwargs["document_id"]] | ||
geom_type = "Polygon" | ||
_id = "geo_id" | ||
|
||
elif export_type == DocumentExportType.block_zone_assignments: | ||
# Sadly, most GeoJSON block exports are too large to go over HTTP | ||
# as a JSON FileResponse. Need to think through a better method. | ||
raise NotImplementedError( | ||
"Block export type is not yet supported for GeoJSON as files are too large" | ||
) | ||
|
||
# stmt = """SELECT * FROM get_block_assignments_geo(%s)""" | ||
# params += [kwargs["document_id"]] | ||
# geom_type = "Polygon" | ||
# _id = "geo_id" | ||
|
||
elif export_type == DocumentExportType.districts: | ||
stmt = """WITH geos AS ( SELECT * FROM get_zone_assignments_geo(%s::UUID) ) | ||
SELECT | ||
zone::TEXT AS zone, | ||
ST_Union(geometry) AS geometry | ||
FROM geos | ||
GROUP BY zone | ||
""" | ||
params += [kwargs["document_id"]] | ||
geom_type = "Polygon" | ||
_id = "zone" | ||
|
||
if not all({stmt, geom_type, _id}): | ||
raise NotImplementedError("Survey export type is not yet supported") | ||
|
||
sql = SQL("""COPY ( | ||
SELECT jsonb_build_object( | ||
'type', 'FeatureCollection', | ||
'features', jsonb_agg(features.feature) ) | ||
FROM ( | ||
SELECT jsonb_build_object( | ||
'type', 'Feature', | ||
'id', {id}, | ||
'geometry', ST_AsGeoJSON(geometry)::jsonb, | ||
'properties', to_jsonb(inputs) - 'geometry' - {id_name} | ||
) AS feature | ||
FROM ( {select} ) inputs ) features ) | ||
TO STDOUT""").format( | ||
id=Identifier("inputs", _id), # pyright: ignore | ||
id_name=Literal(_id), | ||
select=SQL(stmt), # pyright: ignore | ||
) | ||
|
||
return sql, params |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from enum import Enum | ||
|
||
|
||
class DocumentExportFormat(Enum): | ||
csv = "CSV" | ||
geojson = "GeoJSON" | ||
|
||
|
||
class DocumentExportType(Enum): | ||
zone_assignments = "ZoneAssignments" | ||
block_zone_assignments = "BlockZoneAssignments" | ||
districts = "Districts" |
Oops, something went wrong.