Skip to content

Commit

Permalink
Soft delete DistrictrMap (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaellaude authored Nov 18, 2024
1 parent 4539490 commit 60153d5
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 27 deletions.
109 changes: 109 additions & 0 deletions backend/app/alembic/versions/2494caf34886_soft_delete_districtrmap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""soft delete districtrmap
Revision ID: 2494caf34886
Revises: f86991e63a62
Create Date: 2024-11-18 09:37:23.352006
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = "2494caf34886"
down_revision: Union[str, None] = "f86991e63a62"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.add_column(
"districtrmap",
sa.Column("visible", sa.Boolean(), nullable=False, server_default="true"),
)
op.execute(
sa.text(
"""
CREATE OR REPLACE FUNCTION create_districtr_map(
map_name VARCHAR,
gerrydb_table_name VARCHAR,
num_districts INTEGER,
tiles_s3_path VARCHAR,
parent_layer_name VARCHAR,
child_layer_name VARCHAR,
visibility BOOLEAN DEFAULT TRUE
)
RETURNS UUID AS $$
DECLARE
inserted_districtr_uuid UUID;
BEGIN
INSERT INTO districtrmap (
created_at,
uuid,
name,
gerrydb_table_name,
num_districts,
tiles_s3_path,
parent_layer,
child_layer,
visible
)
VALUES (
now(),
gen_random_uuid(),
map_name,
gerrydb_table_name,
num_districts,
tiles_s3_path,
parent_layer_name,
child_layer_name,
visibility
)
RETURNING uuid INTO inserted_districtr_uuid;
RETURN inserted_districtr_uuid;
END;
$$ LANGUAGE plpgsql;
"""
)
)


def downgrade() -> None:
op.drop_column("districtrmap", "visible")
op.execute(
sa.text(
"""
CREATE OR REPLACE FUNCTION create_districtr_map(
map_name VARCHAR,
gerrydb_table_name VARCHAR,
num_districts INTEGER,
tiles_s3_path VARCHAR,
parent_layer_name VARCHAR,
child_layer_name VARCHAR
)
RETURNS UUID AS $$
DECLARE
inserted_districtr_uuid UUID;
BEGIN
INSERT INTO districtrmap (
created_at,
uuid,
name,
gerrydb_table_name,
num_districts,
tiles_s3_path,
parent_layer,
child_layer
)
VALUES (now(), gen_random_uuid(), $1, $2, $3, $4, $5, $6)
RETURNING uuid INTO inserted_districtr_uuid;
RETURN inserted_districtr_uuid;
END;
$$ LANGUAGE plpgsql;
"""
)
)
11 changes: 6 additions & 5 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from fastapi import FastAPI, status, Depends, HTTPException, Query
from sqlalchemy import text
from sqlalchemy.exc import ProgrammingError
from sqlmodel import Session, String, select
from sqlmodel import Session, String, select, true
from starlette.middleware.cors import CORSMiddleware
from sqlalchemy.dialects.postgresql import insert
import logging
Expand Down Expand Up @@ -328,12 +328,12 @@ async def get_summary_stat(
),
SummaryStatsP1,
),
"P4": {
"P4": (
text(
"SELECT * from get_summary_stats_p4(:document_id) WHERE zone is not null"
),
SummaryStatsP4,
},
),
}[summary_stat]
except KeyError:
raise HTTPException(
Expand All @@ -345,7 +345,7 @@ async def get_summary_stat(
results = session.execute(stmt, {"document_id": document_id}).fetchall()
return {
"summary_stat": _summary_stat.value,
"results": [SummaryStatsModel.from_orm(row) for row in results],
"results": [SummaryStatsModel.model_validate(row) for row in results],
}
except ProgrammingError as e:
logger.error(e)
Expand Down Expand Up @@ -390,7 +390,7 @@ async def get_gerrydb_summary_stat(
results = session.execute(stmt, {"gerrydb_table": gerrydb_table}).fetchone()
return {
"summary_stat": _summary_stat.value,
"results": SummaryStatsModel.from_orm(results),
"results": SummaryStatsModel.model_validate(results),
}
except ProgrammingError as e:
logger.error(e)
Expand All @@ -411,6 +411,7 @@ async def get_projects(
):
gerrydb_views = session.exec(
select(DistrictrMap)
.filter(DistrictrMap.visible == true()) # pyright: ignore
.order_by(DistrictrMap.created_at.asc()) # pyright: ignore
.offset(offset)
.limit(limit)
Expand Down
3 changes: 3 additions & 0 deletions backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Column,
MetaData,
String,
Boolean,
)
from sqlalchemy.types import ARRAY, TEXT
from sqlalchemy import Float
Expand Down Expand Up @@ -78,6 +79,7 @@ class DistrictrMap(TimeStampMixin, SQLModel, table=True):
# where does this go?
# when you create the view, pull the columns that you need
# we'll want discrete management steps
visible: bool = Field(sa_column=Column(Boolean, nullable=False, default=True))
available_summary_stats: list[SummaryStatisticType] | None = Field(
sa_column=Column(ARRAY(TEXT), nullable=True, default=[])
)
Expand All @@ -90,6 +92,7 @@ class DistrictrMapPublic(BaseModel):
child_layer: str | None = None
tiles_s3_path: str | None = None
num_districts: int | None = None
visible: bool = True
available_summary_stats: list[str] | None = None


Expand Down
19 changes: 4 additions & 15 deletions backend/app/sql/create_districtr_map_udf.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@ CREATE OR REPLACE FUNCTION create_districtr_map(
num_districts INTEGER,
tiles_s3_path VARCHAR,
parent_layer_name VARCHAR,
child_layer_name VARCHAR
child_layer_name VARCHAR,
visibility BOOLEAN DEFAULT TRUE
)
RETURNS UUID AS $$
DECLARE
inserted_districtr_uuid UUID;
extent GEOMETRY;
BEGIN
EXECUTE format('
SELECT ST_Extent(ST_Transform(geometry, 4326))
FROM gerrydb.%I',
parent_layer_name
) INTO extent;

INSERT INTO districtrmap (
created_at,
uuid,
Expand All @@ -26,7 +20,7 @@ BEGIN
tiles_s3_path,
parent_layer,
child_layer,
extent
visible
)
VALUES (
now(),
Expand All @@ -37,12 +31,7 @@ BEGIN
tiles_s3_path,
parent_layer_name,
child_layer_name,
ARRAY[
ST_XMin(extent),
ST_YMin(extent),
ST_XMax(extent),
ST_YMax(extent)
]
visibility
)
RETURNING uuid INTO inserted_districtr_uuid;

Expand Down
9 changes: 7 additions & 2 deletions backend/app/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sqlalchemy import text
from sqlalchemy import bindparam, Integer, String, Text
from sqlalchemy.types import UUID
from sqlmodel import Session, Float
from sqlmodel import Session, Float, Boolean
import logging


Expand All @@ -19,6 +19,7 @@ def create_districtr_map(
gerrydb_table_name: str | None = None,
num_districts: int | None = None,
tiles_s3_path: str | None = None,
visibility: bool = True,
) -> str:
"""
Create a new districtr map.
Expand All @@ -31,6 +32,7 @@ def create_districtr_map(
gerrydb_table_name: The name of the gerrydb table.
num_districts: The number of districts.
tiles_s3_path: The S3 path to the tiles.
visibility: The visibility of the map.
Returns:
The UUID of the inserted map.
Expand All @@ -44,7 +46,8 @@ def create_districtr_map(
:num_districts,
:tiles_s3_path,
:parent_layer_name,
:child_layer_name
:child_layer_name,
:visibility
)"""
).bindparams(
bindparam(key="map_name", type_=String),
Expand All @@ -53,6 +56,7 @@ def create_districtr_map(
bindparam(key="tiles_s3_path", type_=String),
bindparam(key="parent_layer_name", type_=String),
bindparam(key="child_layer_name", type_=String),
bindparam(key="visibility", type_=Boolean),
)

(inserted_map_uuid,) = session.execute(
Expand All @@ -64,6 +68,7 @@ def create_districtr_map(
"tiles_s3_path": tiles_s3_path,
"parent_layer_name": parent_layer_name,
"child_layer_name": child_layer_name,
"visibility": visibility,
},
)
return inserted_map_uuid # pyright: ignore
Expand Down
4 changes: 1 addition & 3 deletions backend/load_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ def load_sample_data(config):
)
result = session.execute(exists_query).scalar()
if result:
print(
f"###\nMaterialized view {view['gerrydb_table_name']} already exists.\n###"
)
print(f"Materialized view {view['gerrydb_table_name']} already exists.")
else:
subprocess.run(
[
Expand Down
4 changes: 2 additions & 2 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ coverage==7.5.1
cryptography==42.0.5
dnspython==2.6.1
duckdb==0.10.1
fastapi==0.110.0
fastapi==0.115.5
geoalchemy2==0.15.2
h11==0.14.0
# via
Expand Down Expand Up @@ -102,7 +102,7 @@ sqlalchemy==2.0.29
# geoalchemy2
# sqlmodel
sqlmodel==0.0.16
starlette==0.36.3
starlette==0.41.2
# via fastapi
text-unidecode==1.3
typing-extensions==4.10.0
Expand Down
41 changes: 41 additions & 0 deletions backend/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def ks_demo_view_census_blocks_fixture(session: Session):
os.path.join(FIXTURES_PATH, f"{layer}.geojson"),
"-lco",
"OVERWRITE=yes",
"-lco",
"GEOMETRY_NAME=geometry",
"-nln",
f"{GERRY_DB_SCHEMA}.{layer}", # Forced that the layer is imported into the gerrydb schema
],
Expand Down Expand Up @@ -90,6 +92,8 @@ def ks_demo_view_census_blocks_total_vap_fixture(session: Session):
os.path.join(FIXTURES_PATH, f"{layer}.geojson"),
"-lco",
"OVERWRITE=yes",
"-lco",
"GEOMETRY_NAME=geometry",
"-nln",
f"{GERRY_DB_SCHEMA}.{layer}", # Forced that the layer is imported into the gerrydb schema
],
Expand Down Expand Up @@ -131,6 +135,8 @@ def ks_demo_view_census_blocks_no_pop_fixture(session: Session):
os.path.join(FIXTURES_PATH, f"{layer}.geojson"),
"-lco",
"OVERWRITE=yes",
"-lco",
"GEOMETRY_NAME=geometry",
"-nln",
f"{GERRY_DB_SCHEMA}.{layer}", # Forced that the layer is imported into the gerrydb schema
],
Expand Down Expand Up @@ -452,6 +458,41 @@ def test_list_gerydb_views_offset_and_limit(client, districtr_maps):
assert response.status_code == 200
data = response.json()
assert len(data) == 1


@pytest.fixture(name="districtr_maps_soft_deleted")
def districtr_map_soft_deleted_fixture(
session: Session, ks_demo_view_census_blocks_districtrmap: None
):
for i in range(2):
create_districtr_map(
session=session,
name=f"Districtr map {i}",
gerrydb_table_name=f"districtr_map_{i}",
parent_layer_name=GERRY_DB_FIXTURE_NAME,
visibility=bool(
i
), # Should have one hidden (index 0) and one visible (index 1)
)
session.commit()


def test_list_gerydb_views_soft_deleted_map(
client, session, districtr_maps_soft_deleted
):
response = client.get("/api/gerrydb/views")
assert response.status_code == 200
data = response.json()
# One visible from `ks_demo_view_census_blocks_districtrmap`
# One hidden from `districtr_maps_soft_deleted`
# One visible from `districtr_maps_soft_deleted`
assert len(data) == 2

# Check that the hidden map is there
stmt = text("SELECT * FROM districtrmap WHERE not visible")
result = session.execute(stmt).one()
assert result is not None
assert not result[-1] # visible column is False
assert data[0]["name"] == "Districtr map ks_demo_view_census_blocks"


Expand Down

0 comments on commit 60153d5

Please sign in to comment.