From c8f4632af2882328854a1da885ad47caa1e8483b Mon Sep 17 00:00:00 2001 From: Niraj Adhikari <41701707+nrjadkry@users.noreply.github.com> Date: Tue, 3 Oct 2023 01:22:11 +0545 Subject: [PATCH] Alembic Setup (#869) * alembic ini file * alembic migrations env.py file, initial migration files created * commented line to create models on startup * added function to ignore migration if the table exists * updated initial migration file * created alembic helpers file to check if the tables already exists * relative imports * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * build: add alembic to backend deps * build: rebuild pdm.lock after alembic addition * refactor: move alembic files to separate dir * build: refactor: compose unless-stopped --> "unless-stopped" * build: add migration container to compose stack * build: reuse main backend img for migrations * build: backend dockerfile copy all dirs under /opt/ * build: add migration container to prod compose configs * refactor: add additional logging to migrations --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: sam --- docker-compose.deploy.yml | 20 +- docker-compose.noodk.yml | 18 +- docker-compose.yml | 31 +- josm/docker-compose.yml | 4 +- src/backend/.dockerignore | 2 + src/backend/Dockerfile | 5 +- src/backend/alembic.ini | 113 + src/backend/app/db/database.py | 2 +- src/backend/app/db/db_models.py | 3 +- src/backend/app/main.py | 7 +- src/backend/migrations/alembic_helpers.py | 30 + src/backend/migrations/env.py | 93 + src/backend/migrations/script.py.mako | 24 + .../ec28a415c8d8_create_inital_tables.py | 2153 +++++++++++++++++ src/backend/pdm.lock | 46 +- src/backend/pyproject.toml | 4 +- 16 files changed, 2514 insertions(+), 41 deletions(-) create mode 100644 src/backend/alembic.ini create mode 100644 src/backend/migrations/alembic_helpers.py create mode 100644 src/backend/migrations/env.py create mode 100644 src/backend/migrations/script.py.mako create mode 100644 src/backend/migrations/versions/ec28a415c8d8_create_inital_tables.py diff --git a/docker-compose.deploy.yml b/docker-compose.deploy.yml index b100eb6e2f..31aeedc9bd 100644 --- a/docker-compose.deploy.yml +++ b/docker-compose.deploy.yml @@ -40,7 +40,7 @@ services: - 443:443 networks: - fmtm-net - restart: unless-stopped + restart: "unless-stopped" command: - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" @@ -75,7 +75,7 @@ services: - "5433:5432" networks: - fmtm-net - restart: unless-stopped + restart: "unless-stopped" api: image: "ghcr.io/hotosm/fmtm/backend:${API_VERSION}-${GIT_BRANCH}" @@ -96,7 +96,7 @@ services: - .env networks: - fmtm-net - restart: unless-stopped + restart: "unless-stopped" labels: - "traefik.enable=true" - "traefik.http.routers.api.tls=true" @@ -105,6 +105,18 @@ services: - "traefik.http.services.api-svc.loadbalancer.server.port=8000" - "traefik.http.routers.api.service=api-svc" + migrations: + image: "ghcr.io/hotosm/fmtm/backend:${API_VERSION}-${GIT_BRANCH}" + container_name: fmtm_migrations + depends_on: + - api + env_file: + - .env + networks: + - fmtm-net + command: ["alembic", "upgrade", "head"] + restart: "no" + ui-main: image: "ghcr.io/hotosm/fmtm/frontend:${FRONTEND_MAIN_VERSION}-${GIT_BRANCH}" build: @@ -124,7 +136,7 @@ services: - BROTLI=true - API_URL=${URL_SCHEME}://${API_URL} - FRONTEND_MAIN_URL=${URL_SCHEME}://${FRONTEND_MAIN_URL} - restart: unless-stopped + restart: "unless-stopped" labels: - "traefik.enable=true" - "traefik.http.routers.ui-main.tls=true" diff --git a/docker-compose.noodk.yml b/docker-compose.noodk.yml index bfbf273396..19e6e3baf9 100644 --- a/docker-compose.noodk.yml +++ b/docker-compose.noodk.yml @@ -41,7 +41,7 @@ services: - "5433:5432" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" api: image: "ghcr.io/hotosm/fmtm/backend:debug" @@ -65,7 +65,19 @@ services: - "5678:5678" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" + + migrations: + image: "ghcr.io/hotosm/fmtm/backend:${API_VERSION}-${GIT_BRANCH}" + container_name: fmtm_migrations + depends_on: + - api + env_file: + - .env + networks: + - fmtm-net + command: ["alembic", "upgrade", "head"] + restart: "no" ui-main: image: "ghcr.io/hotosm/fmtm/frontend:debug" @@ -89,4 +101,4 @@ services: - "8081:8081" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" diff --git a/docker-compose.yml b/docker-compose.yml index 08af4cab4e..c1f7e7b8b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,7 +42,7 @@ services: - "5438:5432" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" api: image: "ghcr.io/hotosm/fmtm/backend:debug" @@ -72,7 +72,19 @@ services: - "5678:5678" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" + + migrations: + image: "ghcr.io/hotosm/fmtm/backend:debug" + container_name: fmtm_migrations + depends_on: + - api + env_file: + - .env + networks: + - fmtm-dev + command: ["alembic", "upgrade", "head"] + restart: "no" ui-main: image: "ghcr.io/hotosm/fmtm/frontend:debug" @@ -96,7 +108,7 @@ services: - "8080:8080" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" central-db: image: "postgis/postgis:14-3.3-alpine" @@ -111,7 +123,7 @@ services: - "5434:5432" networks: - fmtm-dev - restart: unless-stopped + restart: "unless-stopped" central: image: "ghcr.io/hotosm/fmtm/odkcentral:v2023.2.1" @@ -122,7 +134,6 @@ services: container_name: central_api depends_on: - central-db - - pyxform environment: - DOMAIN=local - SYSADMIN_EMAIL=${ODK_CENTRAL_USER} @@ -154,7 +165,7 @@ services: "--", "./init-user-and-start.sh", ] - restart: unless-stopped + restart: "unless-stopped" central-proxy: image: "ghcr.io/hotosm/fmtm/odkcentral-proxy:latest" @@ -165,10 +176,4 @@ services: - central networks: - fmtm-dev - restart: unless-stopped - - pyxform: - image: "ghcr.io/getodk/pyxform-http:v1.10.1.1" - networks: - - fmtm-dev - restart: always + restart: "unless-stopped" diff --git a/josm/docker-compose.yml b/josm/docker-compose.yml index bd55504678..8c2fd2174c 100644 --- a/josm/docker-compose.yml +++ b/josm/docker-compose.yml @@ -35,7 +35,7 @@ services: - x11 ports: - 8111:80 - restart: unless-stopped + restart: "unless-stopped" josm-novnc: image: "ghcr.io/hotosm/fmtm/josm-novnc:latest" @@ -51,4 +51,4 @@ services: - "8112:8080" networks: - x11 - restart: unless-stopped + restart: "unless-stopped" diff --git a/src/backend/.dockerignore b/src/backend/.dockerignore index 1b6ecc0069..3d82dd92e1 100644 --- a/src/backend/.dockerignore +++ b/src/backend/.dockerignore @@ -8,3 +8,5 @@ !container-entrypoint.sh !pyproject.toml !pdm.lock +!migrations +!alembic.ini diff --git a/src/backend/Dockerfile b/src/backend/Dockerfile index 2303c9c87e..68cef1255c 100644 --- a/src/backend/Dockerfile +++ b/src/backend/Dockerfile @@ -107,6 +107,8 @@ COPY --from=build \ WORKDIR /opt # Add app code COPY app/ /opt/app/ +COPY migrations/ /opt/migrations/ +COPY alembic.ini /opt/ # Add non-root user, permissions RUN useradd -r -u 1001 -m -c "hotosm account" -d /home/appuser -s /bin/false appuser \ && mkdir -p /opt/logs /opt/tiles \ @@ -122,7 +124,6 @@ USER appuser FROM runtime as debug-no-odk -USER appuser COPY --from=extract-deps --chown=appuser \ /opt/python/requirements-debug.txt /opt/python/ RUN pip install --user --upgrade --no-warn-script-location \ @@ -146,7 +147,7 @@ USER appuser -FROM debug-no-odk as ci +FROM runtime as ci # Run all ci as root USER root ENV PATH="/root/.local/bin:$PATH" diff --git a/src/backend/alembic.ini b/src/backend/alembic.ini new file mode 100644 index 0000000000..b2c14b0b03 --- /dev/null +++ b/src/backend/alembic.ini @@ -0,0 +1,113 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Custom param that enables us to specify tables to ignore when determining migrations +[alembic:exclude] +tables = spatial_ref_sys + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = "%(asctime)s.%(msecs)03d [%(levelname)s] %(name)s | %(funcName)s:%(lineno)d | %(message)s" +datefmt = "%y-%m-%d %H:%M:%S" diff --git a/src/backend/app/db/database.py b/src/backend/app/db/database.py index 7c24c42a76..2e0328221a 100644 --- a/src/backend/app/db/database.py +++ b/src/backend/app/db/database.py @@ -22,7 +22,7 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -from ..config import settings +from app.config import settings engine = create_engine(settings.FMTM_DB_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/src/backend/app/db/db_models.py b/src/backend/app/db/db_models.py index 74ba6e682a..77483eefad 100644 --- a/src/backend/app/db/db_models.py +++ b/src/backend/app/db/db_models.py @@ -41,7 +41,7 @@ relationship, ) -from ..models.enums import ( +from app.models.enums import ( BackgroundTaskStatus, MappingLevel, MappingPermission, @@ -55,6 +55,7 @@ UserRole, ValidationPermission, ) + from .database import Base, FmtmMetadata from .postgis_utils import timestamp diff --git a/src/backend/app/main.py b/src/backend/app/main.py index 0373a8fef3..766eeffacb 100644 --- a/src/backend/app/main.py +++ b/src/backend/app/main.py @@ -33,7 +33,7 @@ from .auth import auth_routes from .central import central_routes from .config import settings -from .db.database import Base, engine, get_db +from .db.database import get_db from .organization import organization_routes from .projects import project_routes from .projects.project_crud import read_xlsforms @@ -172,10 +172,7 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE async def startup_event(): """Commands to run on server startup.""" log.debug("Starting up FastAPI server.") - log.debug("Connecting to DB with SQLAlchemy") - Base.metadata.create_all(bind=engine) - - # Read in XLSForms + log.debug("Reading XLSForms from DB.") read_xlsforms(next(get_db()), xlsforms_path) diff --git a/src/backend/migrations/alembic_helpers.py b/src/backend/migrations/alembic_helpers.py new file mode 100644 index 0000000000..2ed288a5a3 --- /dev/null +++ b/src/backend/migrations/alembic_helpers.py @@ -0,0 +1,30 @@ +"""Helpers for alembic migrate and downgrade.""" + +from alembic import op +from sqlalchemy import engine_from_config +from sqlalchemy.engine import reflection + + +def table_does_not_exist(table, schema=None): + """Handle tables that do not exist.""" + config = op.get_context().config + engine = engine_from_config( + config.get_section(config.config_ini_section), prefix="sqlalchemy." + ) + insp = reflection.Inspector.from_engine(engine) + return insp.has_table(table, schema) is False + + +def table_has_column(table, column): + """Handle tables when column already exists.""" + config = op.get_context().config + engine = engine_from_config( + config.get_section(config.config_ini_section), prefix="sqlalchemy." + ) + insp = reflection.Inspector.from_engine(engine) + has_column = False + for col in insp.get_columns(table): + if column not in col["name"]: + continue + has_column = True + return has_column diff --git a/src/backend/migrations/env.py b/src/backend/migrations/env.py new file mode 100644 index 0000000000..6bc495ecc1 --- /dev/null +++ b/src/backend/migrations/env.py @@ -0,0 +1,93 @@ +"""Main alembic migrations file.""" + +from logging import getLogger +from logging.config import fileConfig + +from alembic import context +from geoalchemy2 import alembic_helpers +from sqlalchemy import engine_from_config, pool + +from app.config import settings +from app.db.db_models import Base + +config = context.config +config.set_main_option("sqlalchemy.url", settings.FMTM_DB_URL) + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +target_metadata = Base.metadata +exclude_tables = config.get_section("alembic:exclude").get("tables", "").split(",") +log = getLogger(__name__) + + +def include_object(object, name, type_, reflected, compare_to): + """Ignore our excluded tables in the autogen sweep.""" + if type_ == "table" and name in exclude_tables: + return False + else: + return alembic_helpers.include_object( + object, name, type_, reflected, compare_to + ) + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + log.info("Running offline migrations") + + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + include_object=include_object, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + log.info("Complete offline migrations") + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + log.info("Running online migrations") + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + include_object=include_object, + target_metadata=target_metadata, + ) + + with context.begin_transaction(): + context.run_migrations() + + log.info("Complete online migrations") + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/src/backend/migrations/script.py.mako b/src/backend/migrations/script.py.mako new file mode 100644 index 0000000000..37d0cac313 --- /dev/null +++ b/src/backend/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} \ No newline at end of file diff --git a/src/backend/migrations/versions/ec28a415c8d8_create_inital_tables.py b/src/backend/migrations/versions/ec28a415c8d8_create_inital_tables.py new file mode 100644 index 0000000000..8aa8382f5a --- /dev/null +++ b/src/backend/migrations/versions/ec28a415c8d8_create_inital_tables.py @@ -0,0 +1,2153 @@ +"""create inital tables. + +Revision ID: ec28a415c8d8 +Revises: +Create Date: 2023-09-26 09:04:00.676103 + +""" +import imp +import os + +import geoalchemy2 +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +alembic_helpers = imp.load_source( + "alembic_helpers", + (os.getcwd() + "/" + op.get_context().script.dir + "/alembic_helpers.py"), +) + +# revision identifiers, used by Alembic. +revision = "ec28a415c8d8" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + if alembic_helpers.table_does_not_exist("loader_platform"): + op.create_table( + "loader_platform", + sa.Column("os", sa.VARCHAR(length=50), autoincrement=False, nullable=False), + sa.Column("declare_sect", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("pgbin", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("wget", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("unzip_command", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("psql", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("path_sep", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("loader", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column( + "environ_set_command", sa.TEXT(), autoincrement=False, nullable=True + ), + sa.Column( + "county_process_command", sa.TEXT(), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("os", name="loader_platform_pkey"), + ) + + if alembic_helpers.table_does_not_exist("dumpedpoints"): + op.create_table( + "dumpedpoints", + sa.Column("osm_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("polyid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column("cid", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("clusteruid", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column( + "geom", + geoalchemy2.types.Geometry( + geometry_type="POINT", + srid=4326, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + ) + op.create_index( + "dumpedpoints_idx", + "dumpedpoints", + ["geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("pagc_lex"): + op.create_table( + "pagc_lex", + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column("seq", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("word", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("stdword", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("token", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "is_custom", + sa.BOOLEAN(), + server_default=sa.text("true"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("id", name="pagc_lex_pkey"), + ) + + if alembic_helpers.table_does_not_exist("zip_lookup_all"): + op.create_table( + "zip_lookup_all", + sa.Column("zip", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("st_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "state", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column("co_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "county", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("cs_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "cousub", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("pl_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "place", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("cnt", sa.INTEGER(), autoincrement=False, nullable=True), + ) + + if alembic_helpers.table_does_not_exist("pagc_rules"): + op.create_table( + "pagc_rules", + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column("rule", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column( + "is_custom", + sa.BOOLEAN(), + server_default=sa.text("true"), + autoincrement=False, + nullable=True, + ), + sa.PrimaryKeyConstraint("id", name="pagc_rules_pkey"), + ) + + if alembic_helpers.table_does_not_exist("addrfeat"): + op.create_table( + "addrfeat", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column("tlid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=False + ), + sa.Column( + "aridl", sa.VARCHAR(length=22), autoincrement=False, nullable=True + ), + sa.Column( + "aridr", sa.VARCHAR(length=22), autoincrement=False, nullable=True + ), + sa.Column( + "linearid", sa.VARCHAR(length=22), autoincrement=False, nullable=True + ), + sa.Column( + "fullname", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "lfromhn", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "ltohn", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "rfromhn", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "rtohn", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column("zipl", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column("zipr", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column( + "edge_mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "parityl", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "parityr", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "plus4l", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "plus4r", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "lfromtyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "ltotyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "rfromtyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "rtotyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "offsetl", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "offsetr", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'LINESTRING'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint("gid", name="addrfeat_pkey"), + ) + op.create_index("idx_addrfeat_zipr", "addrfeat", ["zipr"], unique=False) + op.create_index("idx_addrfeat_zipl", "addrfeat", ["zipl"], unique=False) + op.create_index("idx_addrfeat_tlid", "addrfeat", ["tlid"], unique=False) + op.create_index( + "idx_addrfeat_geom_gist", + "addrfeat", + ["the_geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("topology"): + op.create_table( + "topology", + sa.Column( + "id", + sa.INTEGER(), + server_default=sa.text("nextval('topology_id_seq'::regclass)"), + autoincrement=True, + nullable=False, + ), + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("srid", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "precision", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=False, + ), + sa.Column( + "hasz", + sa.BOOLEAN(), + server_default=sa.text("false"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("id", name="topology_pkey"), + sa.UniqueConstraint("name", name="topology_name_key"), + postgresql_ignore_search_path=False, + ) + + if alembic_helpers.table_does_not_exist("featnames"): + op.create_table( + "featnames", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column("tlid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "fullname", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "name", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "predirabrv", sa.VARCHAR(length=15), autoincrement=False, nullable=True + ), + sa.Column( + "pretypabrv", sa.VARCHAR(length=50), autoincrement=False, nullable=True + ), + sa.Column( + "prequalabr", sa.VARCHAR(length=15), autoincrement=False, nullable=True + ), + sa.Column( + "sufdirabrv", sa.VARCHAR(length=15), autoincrement=False, nullable=True + ), + sa.Column( + "suftypabrv", sa.VARCHAR(length=50), autoincrement=False, nullable=True + ), + sa.Column( + "sufqualabr", sa.VARCHAR(length=15), autoincrement=False, nullable=True + ), + sa.Column( + "predir", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "pretyp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "prequal", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "sufdir", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "suftyp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "sufqual", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "linearid", sa.VARCHAR(length=22), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "paflag", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("gid", name="featnames_pkey"), + ) + op.create_index( + "idx_tiger_featnames_tlid_statefp", + "featnames", + ["tlid", "statefp"], + unique=False, + ) + op.create_index( + "idx_tiger_featnames_snd_name", + "featnames", + [sa.text("soundex(name::text)")], + unique=False, + ) + op.create_index( + "idx_tiger_featnames_lname", + "featnames", + [sa.text("lower(name::text)")], + unique=False, + ) + + if alembic_helpers.table_does_not_exist("buildings"): + op.create_table( + "buildings", + sa.Column("id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("project_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("osm_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column( + "geom", + geoalchemy2.types.Geometry( + geometry_type="POLYGON", + srid=4326, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "tags", + postgresql.JSONB(astext_type=sa.Text()), + autoincrement=False, + nullable=True, + ), + sa.Column("polyid", sa.BIGINT(), autoincrement=False, nullable=True), + ) + op.create_index( + "buildings_idx", + "buildings", + ["geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("zcta5"): + op.create_table( + "zcta5", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=False + ), + sa.Column( + "zcta5ce", sa.VARCHAR(length=5), autoincrement=False, nullable=False + ), + sa.Column( + "classfp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "aland", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "awater", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "partflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + spatial_index=False, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint( + "zcta5ce", "statefp", name="pk_tiger_zcta5_zcta5ce" + ), + sa.UniqueConstraint("gid", name="uidx_tiger_zcta5_gid"), + ) + + if alembic_helpers.table_does_not_exist("secondary_unit_lookup"): + op.create_table( + "secondary_unit_lookup", + sa.Column( + "name", sa.VARCHAR(length=20), autoincrement=False, nullable=False + ), + sa.Column( + "abbrev", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("name", name="secondary_unit_lookup_pkey"), + ) + op.create_index( + "secondary_unit_lookup_abbrev_idx", + "secondary_unit_lookup", + ["abbrev"], + unique=False, + ) + + if alembic_helpers.table_does_not_exist("pagc_gaz"): + op.create_table( + "pagc_gaz", + sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column("seq", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("word", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("stdword", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("token", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "is_custom", + sa.BOOLEAN(), + server_default=sa.text("true"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("id", name="pagc_gaz_pkey"), + ) + + if alembic_helpers.table_does_not_exist("direction_lookup"): + op.create_table( + "direction_lookup", + sa.Column( + "name", sa.VARCHAR(length=20), autoincrement=False, nullable=False + ), + sa.Column( + "abbrev", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("name", name="direction_lookup_pkey"), + ) + op.create_index( + "direction_lookup_abbrev_idx", "direction_lookup", ["abbrev"], unique=False + ) + + if alembic_helpers.table_does_not_exist("state_lookup"): + op.create_table( + "state_lookup", + sa.Column("st_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "name", sa.VARCHAR(length=40), autoincrement=False, nullable=True + ), + sa.Column( + "abbrev", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column("statefp", sa.CHAR(length=2), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("st_code", name="state_lookup_pkey"), + sa.UniqueConstraint("abbrev", name="state_lookup_abbrev_key"), + sa.UniqueConstraint("name", name="state_lookup_name_key"), + sa.UniqueConstraint("statefp", name="state_lookup_statefp_key"), + ) + + if alembic_helpers.table_does_not_exist("tabblock20"): + op.create_table( + "tabblock20", + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "tractce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "blockce", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "geoid", sa.VARCHAR(length=15), autoincrement=False, nullable=False + ), + sa.Column( + "name", sa.VARCHAR(length=10), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column("ur", sa.VARCHAR(length=1), autoincrement=False, nullable=True), + sa.Column("uace", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column( + "uatype", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "aland", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "awater", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + geometry_type="MULTIPOLYGON", + srid=4269, + spatial_index=False, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "housing", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "pop", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.PrimaryKeyConstraint("geoid", name="pk_tabblock20"), + ) + + if alembic_helpers.table_does_not_exist("splitpolygons"): + op.create_table( + "splitpolygons", + sa.Column("polyid", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column( + "geom", + geoalchemy2.types.Geometry( + geometry_type="POLYGON", + srid=4326, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "geog", + geoalchemy2.types.Geography( + from_text="ST_GeogFromText", name="geography" + ), + autoincrement=False, + nullable=True, + ), + sa.Column("numfeatures", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "area", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.PrimaryKeyConstraint("polyid", name="splitpolygons_pkey"), + ) + op.create_index( + "splitpolygons_idx", + "splitpolygons", + ["geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("cousub"): + op.create_table( + "cousub", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "cousubfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "cousubns", sa.VARCHAR(length=8), autoincrement=False, nullable=True + ), + sa.Column( + "cosbidfp", sa.VARCHAR(length=10), autoincrement=False, nullable=False + ), + sa.Column( + "name", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "namelsad", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column("lsad", sa.VARCHAR(length=2), autoincrement=False, nullable=True), + sa.Column( + "classfp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "cnectafp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "nectafp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "nctadvfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "aland", + sa.NUMERIC(precision=14, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "awater", + sa.NUMERIC(precision=14, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint("cosbidfp", name="cousub_pkey"), + sa.UniqueConstraint("gid", name="uidx_cousub_gid"), + ) + op.create_index( + "tige_cousub_the_geom_gist", + "cousub", + ["the_geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("zip_state"): + op.create_table( + "zip_state", + sa.Column("zip", sa.VARCHAR(length=5), autoincrement=False, nullable=False), + sa.Column( + "stusps", sa.VARCHAR(length=2), autoincrement=False, nullable=False + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("zip", "stusps", name="zip_state_pkey"), + ) + + if alembic_helpers.table_does_not_exist("place_lookup"): + op.create_table( + "place_lookup", + sa.Column("st_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "state", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column("pl_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "name", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("st_code", "pl_code", name="place_lookup_pkey"), + ) + op.create_index( + "place_lookup_state_idx", "place_lookup", ["state"], unique=False + ) + op.create_index( + "place_lookup_name_idx", + "place_lookup", + [sa.text("soundex(name::text)")], + unique=False, + ) + + if alembic_helpers.table_does_not_exist("lowfeaturecountpolygons"): + op.create_table( + "lowfeaturecountpolygons", + sa.Column("polyid", sa.BIGINT(), autoincrement=False, nullable=False), + sa.Column( + "geom", + geoalchemy2.types.Geometry( + geometry_type="POLYGON", + srid=4326, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "geog", + geoalchemy2.types.Geography( + from_text="ST_GeogFromText", name="geography" + ), + autoincrement=False, + nullable=True, + ), + sa.Column("numfeatures", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "area", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column("n_polyid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "n_area", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column("n_numfeatures", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "sharedbound", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.PrimaryKeyConstraint("polyid", name="lowfeaturecountpolygons_pkey"), + ) + op.create_index( + "lowfeaturecountpolygons_idx", + "lowfeaturecountpolygons", + ["geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("place"): + op.create_table( + "place", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "placefp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "placens", sa.VARCHAR(length=8), autoincrement=False, nullable=True + ), + sa.Column( + "plcidfp", sa.VARCHAR(length=7), autoincrement=False, nullable=False + ), + sa.Column( + "name", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "namelsad", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column("lsad", sa.VARCHAR(length=2), autoincrement=False, nullable=True), + sa.Column( + "classfp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column("cpi", sa.VARCHAR(length=1), autoincrement=False, nullable=True), + sa.Column( + "pcicbsa", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "pcinecta", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column("aland", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column("awater", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint("plcidfp", name="place_pkey"), + sa.UniqueConstraint("gid", name="uidx_tiger_place_gid"), + ) + op.create_index( + "tiger_place_the_geom_gist", + "place", + ["the_geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("zip_state_loc"): + op.create_table( + "zip_state_loc", + sa.Column("zip", sa.VARCHAR(length=5), autoincrement=False, nullable=False), + sa.Column( + "stusps", sa.VARCHAR(length=2), autoincrement=False, nullable=False + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "place", sa.VARCHAR(length=100), autoincrement=False, nullable=False + ), + sa.PrimaryKeyConstraint( + "zip", "stusps", "place", name="zip_state_loc_pkey" + ), + ) + + if alembic_helpers.table_does_not_exist("zip_lookup"): + op.create_table( + "zip_lookup", + sa.Column("zip", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("st_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "state", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column("co_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "county", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("cs_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "cousub", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("pl_code", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "place", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("cnt", sa.INTEGER(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("zip", name="zip_lookup_pkey"), + ) + + if alembic_helpers.table_does_not_exist("county_lookup"): + op.create_table( + "county_lookup", + sa.Column("st_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "state", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column("co_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "name", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("st_code", "co_code", name="county_lookup_pkey"), + ) + op.create_index( + "county_lookup_state_idx", "county_lookup", ["state"], unique=False + ) + op.create_index( + "county_lookup_name_idx", + "county_lookup", + [sa.text("soundex(name::text)")], + unique=False, + ) + + if alembic_helpers.table_does_not_exist("geocode_settings"): + op.create_table( + "geocode_settings", + sa.Column("name", sa.TEXT(), autoincrement=False, nullable=False), + sa.Column("setting", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("unit", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("category", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("short_desc", sa.TEXT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("name", name="geocode_settings_pkey"), + ) + + if alembic_helpers.table_does_not_exist("bg"): + op.create_table( + "bg", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "tractce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "blkgrpce", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "bg_id", sa.VARCHAR(length=12), autoincrement=False, nullable=False + ), + sa.Column( + "namelsad", sa.VARCHAR(length=13), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "aland", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "awater", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + spatial_index=False, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_geom"), + sa.CheckConstraint("st_srid(the_geom) = 4269", name="enforce_srid_geom"), + sa.PrimaryKeyConstraint("bg_id", name="bg_pkey"), + comment="block groups", + ) + + if alembic_helpers.table_does_not_exist("geocode_settings_default"): + op.create_table( + "geocode_settings_default", + sa.Column("name", sa.TEXT(), autoincrement=False, nullable=False), + sa.Column("setting", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("unit", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("category", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("short_desc", sa.TEXT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("name", name="geocode_settings_default_pkey"), + ) + + if alembic_helpers.table_does_not_exist("edges"): + op.create_table( + "edges", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column("tlid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "tfidl", + sa.NUMERIC(precision=10, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "tfidr", + sa.NUMERIC(precision=10, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "fullname", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "smid", sa.VARCHAR(length=22), autoincrement=False, nullable=True + ), + sa.Column( + "lfromadd", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "ltoadd", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "rfromadd", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "rtoadd", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column("zipl", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column("zipr", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column( + "featcat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "hydroflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "railflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "roadflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "olfflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "passflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "divroad", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "exttyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column("ttyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True), + sa.Column( + "deckedroad", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "artpath", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "persist", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "gcseflg", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "offsetl", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "offsetr", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "tnidf", + sa.NUMERIC(precision=10, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "tnidt", + sa.NUMERIC(precision=10, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTILINESTRING'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint("gid", name="edges_pkey"), + ) + op.create_index( + "idx_tiger_edges_the_geom_gist", + "edges", + ["the_geom"], + unique=False, + postgresql_using="gist", + ) + op.create_index("idx_tiger_edges_countyfp", "edges", ["countyfp"], unique=False) + op.create_index("idx_edges_tlid", "edges", ["tlid"], unique=False) + + if alembic_helpers.table_does_not_exist("faces"): + op.create_table( + "faces", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "tfid", + sa.NUMERIC(precision=10, scale=0), + autoincrement=False, + nullable=True, + ), + sa.Column( + "statefp00", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp00", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "tractce00", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "blkgrpce00", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "blockce00", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "cousubfp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "submcdfp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "conctyfp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "placefp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "aiannhfp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "aiannhce00", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "comptyp00", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "trsubfp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "trsubce00", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "anrcfp00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "elsdlea00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "scsdlea00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "unsdlea00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "uace00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "cd108fp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "sldust00", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "sldlst00", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "vtdst00", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "zcta5ce00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "tazce00", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "ugace00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "puma5ce00", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "tractce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "blkgrpce", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "blockce", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "cousubfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "submcdfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "conctyfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "placefp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "aiannhfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "aiannhce", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "comptyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "trsubfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "trsubce", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "anrcfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "ttractce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "tblkgpce", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "elsdlea", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "scsdlea", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "unsdlea", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column("uace", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column( + "cd111fp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "sldust", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "sldlst", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "vtdst", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "zcta5ce", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "tazce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "ugace", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "puma5ce", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "csafp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "cbsafp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "metdivfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "cnectafp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "nectafp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "nctadvfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "lwflag", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "offset", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "atotal", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "tractce20", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "blkgrpce20", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "blockce20", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp20", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "statefp20", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint("gid", name="faces_pkey"), + ) + op.create_index( + "tiger_faces_the_geom_gist", + "faces", + ["the_geom"], + unique=False, + postgresql_using="gist", + ) + op.create_index("idx_tiger_faces_tfid", "faces", ["tfid"], unique=False) + op.create_index("idx_tiger_faces_countyfp", "faces", ["countyfp"], unique=False) + + if alembic_helpers.table_does_not_exist("loader_variables"): + op.create_table( + "loader_variables", + sa.Column( + "tiger_year", sa.VARCHAR(length=4), autoincrement=False, nullable=False + ), + sa.Column("website_root", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("staging_fold", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("data_schema", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column("staging_schema", sa.TEXT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint("tiger_year", name="loader_variables_pkey"), + ) + + if alembic_helpers.table_does_not_exist("county"): + op.create_table( + "county", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "countyns", sa.VARCHAR(length=8), autoincrement=False, nullable=True + ), + sa.Column( + "cntyidfp", sa.VARCHAR(length=5), autoincrement=False, nullable=False + ), + sa.Column( + "name", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column( + "namelsad", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column("lsad", sa.VARCHAR(length=2), autoincrement=False, nullable=True), + sa.Column( + "classfp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "csafp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "cbsafp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "metdivfp", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column("aland", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "awater", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + spatial_index=False, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_geom"), + sa.CheckConstraint("st_srid(the_geom) = 4269", name="enforce_srid_geom"), + sa.PrimaryKeyConstraint("cntyidfp", name="pk_tiger_county"), + sa.UniqueConstraint("gid", name="uidx_county_gid"), + ) + op.create_index("idx_tiger_county", "county", ["countyfp"], unique=False) + + if alembic_helpers.table_does_not_exist("zip_lookup_base"): + op.create_table( + "zip_lookup_base", + sa.Column("zip", sa.VARCHAR(length=5), autoincrement=False, nullable=False), + sa.Column( + "state", sa.VARCHAR(length=40), autoincrement=False, nullable=True + ), + sa.Column( + "county", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column( + "city", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("zip", name="zip_lookup_base_pkey"), + ) + + if alembic_helpers.table_does_not_exist("tract"): + op.create_table( + "tract", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "tractce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "tract_id", sa.VARCHAR(length=11), autoincrement=False, nullable=False + ), + sa.Column("name", sa.VARCHAR(length=7), autoincrement=False, nullable=True), + sa.Column( + "namelsad", sa.VARCHAR(length=20), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "aland", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "awater", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + spatial_index=False, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_geom"), + sa.CheckConstraint("st_srid(the_geom) = 4269", name="enforce_srid_geom"), + sa.PrimaryKeyConstraint("tract_id", name="tract_pkey"), + ) + + if alembic_helpers.table_does_not_exist("layer"): + op.create_table( + "layer", + sa.Column("topology_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("layer_id", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column("schema_name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column("table_name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column( + "feature_column", sa.VARCHAR(), autoincrement=False, nullable=False + ), + sa.Column( + "feature_type", sa.INTEGER(), autoincrement=False, nullable=False + ), + sa.Column( + "level", + sa.INTEGER(), + server_default=sa.text("0"), + autoincrement=False, + nullable=False, + ), + sa.Column("child_id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint( + ["topology_id"], ["topology.id"], name="layer_topology_id_fkey" + ), + sa.PrimaryKeyConstraint("topology_id", "layer_id", name="layer_pkey"), + sa.UniqueConstraint( + "schema_name", + "table_name", + "feature_column", + name="layer_schema_name_table_name_feature_column_key", + ), + ) + + if alembic_helpers.table_does_not_exist("street_type_lookup"): + op.create_table( + "street_type_lookup", + sa.Column( + "name", sa.VARCHAR(length=50), autoincrement=False, nullable=False + ), + sa.Column( + "abbrev", sa.VARCHAR(length=50), autoincrement=False, nullable=True + ), + sa.Column( + "is_hw", + sa.BOOLEAN(), + server_default=sa.text("false"), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("name", name="street_type_lookup_pkey"), + ) + op.create_index( + "street_type_lookup_abbrev_idx", + "street_type_lookup", + ["abbrev"], + unique=False, + ) + + if alembic_helpers.table_does_not_exist("taskpolygons"): + op.create_table( + "taskpolygons", + sa.Column( + "geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column("clusteruid", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.create_index( + "taskpolygons_idx", + "taskpolygons", + ["geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("tabblock"): + op.create_table( + "tabblock", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "countyfp", sa.VARCHAR(length=3), autoincrement=False, nullable=True + ), + sa.Column( + "tractce", sa.VARCHAR(length=6), autoincrement=False, nullable=True + ), + sa.Column( + "blockce", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "tabblock_id", + sa.VARCHAR(length=16), + autoincrement=False, + nullable=False, + ), + sa.Column( + "name", sa.VARCHAR(length=20), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column("ur", sa.VARCHAR(length=1), autoincrement=False, nullable=True), + sa.Column("uace", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "aland", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "awater", + sa.DOUBLE_PRECISION(precision=53), + autoincrement=False, + nullable=True, + ), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + spatial_index=False, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_geom"), + sa.CheckConstraint("st_srid(the_geom) = 4269", name="enforce_srid_geom"), + sa.PrimaryKeyConstraint("tabblock_id", name="tabblock_pkey"), + ) + + if alembic_helpers.table_does_not_exist("countysub_lookup"): + op.create_table( + "countysub_lookup", + sa.Column("st_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "state", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column("co_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "county", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.Column("cs_code", sa.INTEGER(), autoincrement=False, nullable=False), + sa.Column( + "name", sa.VARCHAR(length=90), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint( + "st_code", "co_code", "cs_code", name="countysub_lookup_pkey" + ), + ) + op.create_index( + "countysub_lookup_state_idx", "countysub_lookup", ["state"], unique=False + ) + op.create_index( + "countysub_lookup_name_idx", + "countysub_lookup", + [sa.text("soundex(name::text)")], + unique=False, + ) + + if alembic_helpers.table_does_not_exist("addr"): + op.create_table( + "addr", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column("tlid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "fromhn", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "tohn", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column("side", sa.VARCHAR(length=1), autoincrement=False, nullable=True), + sa.Column("zip", sa.VARCHAR(length=5), autoincrement=False, nullable=True), + sa.Column( + "plus4", sa.VARCHAR(length=4), autoincrement=False, nullable=True + ), + sa.Column( + "fromtyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column( + "totyp", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column("fromarmid", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("toarmid", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column( + "arid", sa.VARCHAR(length=22), autoincrement=False, nullable=True + ), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.PrimaryKeyConstraint("gid", name="addr_pkey"), + ) + op.create_index("idx_tiger_addr_zip", "addr", ["zip"], unique=False) + op.create_index( + "idx_tiger_addr_tlid_statefp", "addr", ["tlid", "statefp"], unique=False + ) + + if alembic_helpers.table_does_not_exist("voronois"): + op.create_table( + "voronois", + sa.Column("clusteruid", sa.TEXT(), autoincrement=False, nullable=True), + sa.Column( + "geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + ) + op.create_index( + "voronois_idx", "voronois", ["geom"], unique=False, postgresql_using="gist" + ) + + if alembic_helpers.table_does_not_exist("state"): + op.create_table( + "state", + sa.Column("gid", sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column( + "region", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "division", sa.VARCHAR(length=2), autoincrement=False, nullable=True + ), + sa.Column( + "statefp", sa.VARCHAR(length=2), autoincrement=False, nullable=False + ), + sa.Column( + "statens", sa.VARCHAR(length=8), autoincrement=False, nullable=True + ), + sa.Column( + "stusps", sa.VARCHAR(length=2), autoincrement=False, nullable=False + ), + sa.Column( + "name", sa.VARCHAR(length=100), autoincrement=False, nullable=True + ), + sa.Column("lsad", sa.VARCHAR(length=2), autoincrement=False, nullable=True), + sa.Column( + "mtfcc", sa.VARCHAR(length=5), autoincrement=False, nullable=True + ), + sa.Column( + "funcstat", sa.VARCHAR(length=1), autoincrement=False, nullable=True + ), + sa.Column("aland", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column("awater", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column( + "intptlat", sa.VARCHAR(length=11), autoincrement=False, nullable=True + ), + sa.Column( + "intptlon", sa.VARCHAR(length=12), autoincrement=False, nullable=True + ), + sa.Column( + "the_geom", + geoalchemy2.types.Geometry( + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.CheckConstraint( + "geometrytype(the_geom) = 'MULTIPOLYGON'::text OR the_geom IS NULL", + name="enforce_geotype_the_geom", + ), + sa.CheckConstraint("st_ndims(the_geom) = 2", name="enforce_dims_the_geom"), + sa.CheckConstraint( + "st_srid(the_geom) = 4269", name="enforce_srid_the_geom" + ), + sa.PrimaryKeyConstraint("statefp", name="pk_tiger_state"), + sa.UniqueConstraint("gid", name="uidx_tiger_state_gid"), + sa.UniqueConstraint("stusps", name="uidx_tiger_state_stusps"), + ) + op.create_index( + "idx_tiger_state_the_geom_gist", + "state", + ["the_geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("clusteredbuildings"): + op.create_table( + "clusteredbuildings", + sa.Column("id", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("project_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column("osm_id", sa.VARCHAR(), autoincrement=False, nullable=True), + sa.Column( + "geom", + geoalchemy2.types.Geometry( + geometry_type="POLYGON", + srid=4326, + from_text="ST_GeomFromEWKT", + name="geometry", + _spatial_index_reflected=True, + ), + autoincrement=False, + nullable=True, + ), + sa.Column( + "tags", + postgresql.JSONB(astext_type=sa.Text()), + autoincrement=False, + nullable=True, + ), + sa.Column("polyid", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column("numfeatures", sa.BIGINT(), autoincrement=False, nullable=True), + sa.Column("cid", sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column("clusteruid", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.create_index( + "clusteredbuildings_idx", + "clusteredbuildings", + ["geom"], + unique=False, + postgresql_using="gist", + ) + + if alembic_helpers.table_does_not_exist("loader_lookuptables"): + op.create_table( + "loader_lookuptables", + sa.Column( + "process_order", + sa.INTEGER(), + server_default=sa.text("1000"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "lookup_name", + sa.TEXT(), + autoincrement=False, + nullable=False, + comment="This is the table name to inherit from and suffix of resulting output table -- how the table will be named -- edges here would mean -- ma_edges , pa_edges etc. except in the case of national tables. national level tables have no prefix", + ), + sa.Column( + "table_name", + sa.TEXT(), + autoincrement=False, + nullable=True, + comment="suffix of the tables to load e.g. edges would load all tables like *edges.dbf(shp) -- so tl_2010_42129_edges.dbf . ", + ), + sa.Column( + "single_mode", + sa.BOOLEAN(), + server_default=sa.text("true"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "load", + sa.BOOLEAN(), + server_default=sa.text("true"), + autoincrement=False, + nullable=False, + comment="Whether or not to load the table. For states and zcta5 (you may just want to download states10, zcta510 nationwide file manually) load your own into a single table that inherits from tiger.states, tiger.zcta5. You'll get improved performance for some geocoding cases.", + ), + sa.Column( + "level_county", + sa.BOOLEAN(), + server_default=sa.text("false"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "level_state", + sa.BOOLEAN(), + server_default=sa.text("false"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "level_nation", + sa.BOOLEAN(), + server_default=sa.text("false"), + autoincrement=False, + nullable=False, + comment="These are tables that contain all data for the whole US so there is just a single file", + ), + sa.Column( + "post_load_process", sa.TEXT(), autoincrement=False, nullable=True + ), + sa.Column( + "single_geom_mode", + sa.BOOLEAN(), + server_default=sa.text("false"), + autoincrement=False, + nullable=True, + ), + sa.Column( + "insert_mode", + sa.CHAR(length=1), + server_default=sa.text("'c'::bpchar"), + autoincrement=False, + nullable=False, + ), + sa.Column( + "pre_load_process", sa.TEXT(), autoincrement=False, nullable=True + ), + sa.Column( + "columns_exclude", + postgresql.ARRAY(sa.TEXT()), + autoincrement=False, + nullable=True, + comment="List of columns to exclude as an array. This is excluded from both input table and output table and rest of columns remaining are assumed to be in same order in both tables. gid, geoid,cpi,suffix1ce are excluded if no columns are specified.", + ), + sa.Column( + "website_root_override", + sa.TEXT(), + autoincrement=False, + nullable=True, + comment="Path to use for wget instead of that specified in year table. Needed currently for zcta where they release that only for 2000 and 2010", + ), + sa.PrimaryKeyConstraint("lookup_name", name="loader_lookuptables_pkey"), + ) + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("loader_lookuptables") + op.drop_index( + "clusteredbuildings_idx", + table_name="clusteredbuildings", + postgresql_using="gist", + ) + op.drop_table("clusteredbuildings") + op.drop_index( + "idx_tiger_state_the_geom_gist", table_name="state", postgresql_using="gist" + ) + op.drop_table("state") + op.drop_index("voronois_idx", table_name="voronois", postgresql_using="gist") + op.drop_table("voronois") + op.drop_index("idx_tiger_addr_tlid_statefp", table_name="addr") + op.drop_index("idx_tiger_addr_zip", table_name="addr") + op.drop_table("addr") + op.drop_index("countysub_lookup_name_idx", table_name="countysub_lookup") + op.drop_index("countysub_lookup_state_idx", table_name="countysub_lookup") + op.drop_table("countysub_lookup") + op.drop_table("tabblock") + op.drop_index( + "taskpolygons_idx", table_name="taskpolygons", postgresql_using="gist" + ) + op.drop_table("taskpolygons") + op.drop_index("street_type_lookup_abbrev_idx", table_name="street_type_lookup") + op.drop_table("street_type_lookup") + op.drop_table("layer") + op.drop_table("tract") + op.drop_table("zip_lookup_base") + op.drop_index("idx_tiger_county", table_name="county") + op.drop_table("county") + op.drop_table("loader_variables") + op.drop_index("idx_tiger_faces_countyfp", table_name="faces") + op.drop_index("idx_tiger_faces_tfid", table_name="faces") + op.drop_index( + "tiger_faces_the_geom_gist", table_name="faces", postgresql_using="gist" + ) + op.drop_table("faces") + op.drop_index("idx_edges_tlid", table_name="edges") + op.drop_index("idx_tiger_edges_countyfp", table_name="edges") + op.drop_index( + "idx_tiger_edges_the_geom_gist", table_name="edges", postgresql_using="gist" + ) + op.drop_table("edges") + op.drop_table("geocode_settings_default") + op.drop_table("bg") + op.drop_table("geocode_settings") + op.drop_index("county_lookup_name_idx", table_name="county_lookup") + op.drop_index("county_lookup_state_idx", table_name="county_lookup") + op.drop_table("county_lookup") + op.drop_table("zip_lookup") + op.drop_table("zip_state_loc") + op.drop_index( + "tiger_place_the_geom_gist", table_name="place", postgresql_using="gist" + ) + op.drop_table("place") + op.drop_index( + "lowfeaturecountpolygons_idx", + table_name="lowfeaturecountpolygons", + postgresql_using="gist", + ) + op.drop_table("lowfeaturecountpolygons") + op.drop_index("place_lookup_name_idx", table_name="place_lookup") + op.drop_index("place_lookup_state_idx", table_name="place_lookup") + op.drop_table("place_lookup") + op.drop_table("zip_state") + op.drop_index( + "tige_cousub_the_geom_gist", table_name="cousub", postgresql_using="gist" + ) + op.drop_table("cousub") + op.drop_index( + "splitpolygons_idx", table_name="splitpolygons", postgresql_using="gist" + ) + op.drop_table("splitpolygons") + op.drop_table("tabblock20") + op.drop_table("state_lookup") + op.drop_index("direction_lookup_abbrev_idx", table_name="direction_lookup") + op.drop_table("direction_lookup") + op.drop_table("pagc_gaz") + op.drop_index( + "secondary_unit_lookup_abbrev_idx", table_name="secondary_unit_lookup" + ) + op.drop_table("secondary_unit_lookup") + op.drop_table("zcta5") + op.drop_index("buildings_idx", table_name="buildings", postgresql_using="gist") + op.drop_table("buildings") + op.drop_index("idx_tiger_featnames_lname", table_name="featnames") + op.drop_index("idx_tiger_featnames_snd_name", table_name="featnames") + op.drop_index("idx_tiger_featnames_tlid_statefp", table_name="featnames") + op.drop_table("featnames") + op.drop_table("topology") + op.drop_index( + "idx_addrfeat_geom_gist", table_name="addrfeat", postgresql_using="gist" + ) + op.drop_index("idx_addrfeat_tlid", table_name="addrfeat") + op.drop_index("idx_addrfeat_zipl", table_name="addrfeat") + op.drop_index("idx_addrfeat_zipr", table_name="addrfeat") + op.drop_table("addrfeat") + op.drop_table("pagc_rules") + op.drop_table("zip_lookup_all") + op.drop_table("pagc_lex") + op.drop_index( + "dumpedpoints_idx", table_name="dumpedpoints", postgresql_using="gist" + ) + op.drop_table("dumpedpoints") + op.drop_table("loader_platform") + # ### end Alembic commands ### diff --git a/src/backend/pdm.lock b/src/backend/pdm.lock index 47ede9e909..938fe14508 100644 --- a/src/backend/pdm.lock +++ b/src/backend/pdm.lock @@ -2,11 +2,26 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "debug", "dev", "docs", "test"] +groups = ["default", "test", "dev", "docs", "debug"] cross_platform = true static_urls = false lock_version = "4.3" -content_hash = "sha256:507fefabdf6bb662691e6652dd1beef9820d5921e89c24f7b04c1ff9e78a67df" +content_hash = "sha256:f074e7e235d93874f1308bcdbdd80f830ce811de6b151534b4ca6e768c3cae41" + +[[package]] +name = "alembic" +version = "1.12.0" +requires_python = ">=3.7" +summary = "A database migration tool for SQLAlchemy." +dependencies = [ + "Mako", + "SQLAlchemy>=1.3.0", + "typing-extensions>=4", +] +files = [ + {file = "alembic-1.12.0-py3-none-any.whl", hash = "sha256:03226222f1cf943deee6c85d9464261a6c710cd19b4fe867a3ad1f25afda610f"}, + {file = "alembic-1.12.0.tar.gz", hash = "sha256:8e7645c32e4f200675e69f0745415335eb59a3663f5feb487abfa0b30c45888b"}, +] [[package]] name = "annotated-types" @@ -693,6 +708,19 @@ files = [ {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, ] +[[package]] +name = "mako" +version = "1.2.4" +requires_python = ">=3.7" +summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." +dependencies = [ + "MarkupSafe>=0.9.2", +] +files = [ + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, +] + [[package]] name = "markdown" version = "3.4.4" @@ -992,7 +1020,7 @@ name = "osm-login-python" version = "1.0.1" requires_python = ">=3.9" git = "https://github.com/hotosm/osm-login-python" -revision = "9085757812f4e3cc4ec11b213574dcd8bb8e9b79" +revision = "a396d081bfc31afc949108508e2184ef7763d954" summary = "Use OSM Token exchange with OAuth2.0 for python projects." dependencies = [ "itsdangerous~=2.1.2", @@ -1482,7 +1510,7 @@ files = [ name = "questionary" version = "2.0.1" requires_python = ">=3.8" -summary = "Python library to build pretty command line user prompts ⭐️" +summary = "Python library to build pretty command line user prompts â­�ï¸�" dependencies = [ "prompt-toolkit<=3.0.36,>=2.0", ] @@ -1820,12 +1848,12 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.7.1" +requires_python = ">=3.7" +summary = "Backported and Experimental Type Hints for Python 3.7+" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index ef92799b37..befb60dd8a 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -30,14 +30,16 @@ dependencies = [ "pydantic-settings>=2.0.3", "geojson-pydantic==1.0.0", "python-multipart>=0.0.6", + "sqlalchemy>=2.0.21", + "SQLAlchemy-Utils==0.41.1", "psycopg2==2.9.7", "geoalchemy2==0.14.1", + "alembic>=1.12.0", "geojson==3.0.1", "shapely==2.0.1", "pyxform==1.12.1", "qrcode==7.4.2", "xmltodict==0.13.0", - "SQLAlchemy-Utils==0.41.1", "segno==1.5.2", "osm-fieldwork==0.3.6rc1", "sentry-sdk==1.30.0",