From 956147aea8eea2870174ddff3bf988c30f4c74f7 Mon Sep 17 00:00:00 2001 From: phil Date: Wed, 27 Dec 2023 01:11:54 +0530 Subject: [PATCH] Fix api stores Rename in category table: geom_type to gis_type --- src/gisaf/api.py | 4 ++-- src/gisaf/importers.py | 4 ++-- src/gisaf/models/category.py | 8 +++---- src/gisaf/models/geo_models_base.py | 3 +++ src/gisaf/models/store.py | 10 ++++---- src/gisaf/redis_tools.py | 2 +- src/gisaf/registry.py | 36 ++++++++++++++++++++++------- 7 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/gisaf/api.py b/src/gisaf/api.py index c3c0e87..fb42e2b 100644 --- a/src/gisaf/api.py +++ b/src/gisaf/api.py @@ -6,7 +6,6 @@ from fastapi import Depends, FastAPI, HTTPException, status, responses from sqlalchemy.orm import selectinload from fastapi.security import OAuth2PasswordRequestForm from sqlmodel import select -from sqlmodel.ext.asyncio.session import AsyncSession from gisaf.models.authentication import ( User, UserRead, @@ -105,7 +104,8 @@ async def get_categories_p( @api.get("/stores") async def get_stores() -> list[Store]: - df = registry.stores.reset_index().drop(columns=['model', 'raw_model']) + df = registry.stores.reset_index().\ + drop(columns=['model', 'raw_model', 'base_gis_type']) return df.to_dict(orient="records") diff --git a/src/gisaf/importers.py b/src/gisaf/importers.py index 42b639d..e2b24d6 100644 --- a/src/gisaf/importers.py +++ b/src/gisaf/importers.py @@ -205,7 +205,7 @@ class RawSurveyImporter(Importer): gdf['geom'] = gdf.to_crs(conf.srid).geometry.apply(lambda g: dumps_wkb(g, srid=conf.srid, hex=True)) for category_name, category_gdf in gdf.groupby('category'): category = registry.categories.loc[category_name] - if category.auto_import and category.geom_type == 'Point': + if category.auto_import and category.gis_type == 'Point': if not dry_run: await upsert_df(category_gdf, category.model) @@ -451,7 +451,7 @@ class GeoDataImporter(Importer): await upsert_df(new_tags, Tags) ## Publish on Gisaf live - gis_type = gdf_file.geom_type.iloc[0] + gis_type = gdf_file.gis_type.iloc[0] mapbox_type = SHAPELY_TYPE_TO_MAPBOX_TYPE.get(gis_type, None) mapbox_paint = DEFAULT_MAPBOX_PAINT.get(mapbox_type, {}).copy() mapbox_layout = DEFAULT_MAPBOX_LAYOUT.get(mapbox_type, {}).copy() diff --git a/src/gisaf/models/category.py b/src/gisaf/models/category.py index da74e39..8045af6 100644 --- a/src/gisaf/models/category.py +++ b/src/gisaf/models/category.py @@ -60,9 +60,9 @@ class CategoryBase(BaseModel): status: str = Field(min_length=1, max_length=1) custom: bool | None auto_import: bool = True - geom_type: str = Field(max_length=50, - foreign_key='category_model_type.name', - default='Point') + gis_type: str = Field(max_length=50, + foreign_key='category_model_type.name', + default='Point') long_name: str | None = Field(max_length=50) style: str | None = Field(sa_type=TEXT) symbol: str | None = Field(max_length=1) @@ -108,7 +108,7 @@ class CategoryBase(BaseModel): @computed_field @property def mapbox_type(self) -> str: - return self.mapbox_type_custom or mapbox_type_mapping[self.geom_type] + return self.mapbox_type_custom or mapbox_type_mapping[self.gis_type] class Category(CategoryBase, table=True): diff --git a/src/gisaf/models/geo_models_base.py b/src/gisaf/models/geo_models_base.py index 29c0ddd..8533086 100644 --- a/src/gisaf/models/geo_models_base.py +++ b/src/gisaf/models/geo_models_base.py @@ -287,6 +287,9 @@ class GeoModel(Model): Base class for all geo models """ #__abstract__ = True + + id: int | None = Field(default=None, primary_key=True) + description: ClassVar[str] = '' attribution: ClassVar[str | None] = None diff --git a/src/gisaf/models/store.py b/src/gisaf/models/store.py index b41be13..9718116 100644 --- a/src/gisaf/models/store.py +++ b/src/gisaf/models/store.py @@ -8,7 +8,7 @@ class MapLibreStyle(BaseModel): class Store(BaseModel): auto_import: bool - base_gis_type: str + # base_gis_type: str count: int custom: bool description: str @@ -23,12 +23,12 @@ class Store(BaseModel): #mapbox_layout: dict[str, Any] | None #mapbox_paint: dict[str, Any] | None #mapbox_type: str - mapbox_type_custom: str | None + # mapbox_type_custom: str | None #mapbox_type_default: str - minor_group_1: str - minor_group_2: str + minor_group_1: str | None + minor_group_2: str | None #model: GeoModel - geom_type: str + gis_type: str name: str #name_letter: str #name_number: int diff --git a/src/gisaf/redis_tools.py b/src/gisaf/redis_tools.py index 3b31427..7d8e274 100644 --- a/src/gisaf/redis_tools.py +++ b/src/gisaf/redis_tools.py @@ -190,7 +190,7 @@ class Store: gdf['popup'] = 'Live: ' + live_name + ' #' + gdf.index.astype('U') if len(gdf) > 0: gdf = gdf.to_crs(conf.crs.geojson) - gis_type = gdf.geom_type.iloc[0] + gis_type = gdf.gis_type.iloc[0] else: gis_type = 'Point' ## FIXME: cannot be inferred from the gdf? mapbox_type = SHAPELY_TYPE_TO_MAPBOX_TYPE.get(gis_type, None) diff --git a/src/gisaf/registry.py b/src/gisaf/registry.py index 553eeb6..cb5638d 100644 --- a/src/gisaf/registry.py +++ b/src/gisaf/registry.py @@ -14,6 +14,7 @@ from sqlalchemy import text from sqlalchemy.orm import selectinload from sqlmodel import SQLModel, select, inspect import pandas as pd +import numpy as np from gisaf.config import conf from gisaf.models import (misc, category as category_module, @@ -166,7 +167,7 @@ class ModelRegistry: else: logger.debug('Discovered {:s}'.format(category.raw_survey_table_name)) - model_class = category_model_mapper.get(category.geom_type) + model_class = category_model_mapper.get(category.gis_type) ## Final geometries try: if model_class: @@ -213,13 +214,13 @@ class ModelRegistry: obj = getattr(module, name) if hasattr(obj, '__module__') and obj.__module__.startswith(module.__name__)\ and hasattr(obj, '__tablename__') and hasattr(obj, 'get_store_name'): - geom_type = self.add_model(obj) - logger.debug(f'Model {obj.get_store_name()} added in the registry from gisaf source tree as {geom_type}') + gis_type = self.add_model(obj) + logger.debug(f'Model {obj.get_store_name()} added in the registry from gisaf source tree as {gis_type}') ## Scan the models defined in plugins (setuptools' entry points) for module_name, model in self.scan_entry_points(name='gisaf_extras.models').items(): - geom_type = self.add_model(model) - logger.debug(f'Model {model.get_store_name()} added in the registry from {module_name} entry point as {geom_type}') + gis_type = self.add_model(model) + logger.debug(f'Model {model.get_store_name()} added in the registry from {module_name} entry point as {gis_type}') for module_name, store in self.scan_entry_points(name='gisaf_extras.stores').items(): self.add_store(store) @@ -362,11 +363,15 @@ class ModelRegistry: Used in GraphQl queries. """ ## Utility functions used with apply method (dataframes) - def fill_columns_from_custom_models(row) -> tuple[str, str, str]: + def fill_columns_from_custom_models(row) -> tuple[str, str, str, str, str, str, str]: return ( + row.model.__name__, row.model.__name__, row.model.description, - row.model.metadata.schema + row.model.metadata.schema, + row.model.base_gis_type, + row.model.style, + row.model.symbol, ) def fill_columns_from_custom_stores(row) -> tuple[str, str, None]: @@ -430,7 +435,9 @@ class ModelRegistry: self.categories['is_line_work'] = None self.categories['raw_survey_model'] = None + ## -------------------- ## Custom models (Misc) + ## -------------------- self.custom_models = pd.DataFrame( self.geom_custom.items(), columns=['store', 'model'] @@ -446,17 +453,27 @@ class ModelRegistry: self.custom_models = self.custom_models.loc[self.custom_models.in_menu] self.custom_models['auto_import'] = False self.custom_models['is_line_work'] = False + self.custom_models['minor_group_1'] = None + self.custom_models['minor_group_2'] = None if len(self.custom_models) > 0: + self.custom_models['name'],\ self.custom_models['long_name'],\ self.custom_models['custom_description'],\ self.custom_models['db_schema'],\ + self.custom_models['gis_type'],\ + self.custom_models['style'],\ + self.custom_models['symbol'],\ = zip(*self.custom_models.apply(fill_columns_from_custom_models, axis=1)) ## Try to give a meaningful description, eg. including the source (db_schema) self.custom_models['description'] = self.custom_models['custom_description'].fillna(self.custom_models['long_name'] + '-' + self.custom_models['db_schema']) self.custom_models['title'] = self.custom_models['long_name'] + self.custom_models.fillna(np.nan, inplace=True) + self.custom_models.replace([np.nan], [None], inplace=True) + ## ------------------------- ## Custom stores (Community) + ## ------------------------- self.custom_stores = pd.DataFrame( self.geom_custom_store.items(), columns=['store', 'model'] @@ -481,6 +498,8 @@ class ModelRegistry: self.custom_stores['db_schema'],\ = zip(*self.custom_stores.apply(fill_columns_from_custom_stores, axis=1)) self.custom_stores['title'] = self.custom_stores['long_name'] + self.custom_stores.fillna(np.nan, inplace=True) + self.custom_stores.replace([np.nan], [None], inplace=True) ## Combine Misc (custom) and survey (auto) stores ## Retain only one status per category (defaultStatus, 'E'/existing by default) @@ -490,6 +509,7 @@ class ModelRegistry: self.custom_stores ])#.drop(columns=['store_name']) self.stores['in_menu'] = self.stores['in_menu'].astype(bool) + self.stores['status'].fillna('E', inplace=True) ## Set in the stores dataframe some useful properties, from the model class ## Maybe at some point it makes sense to get away from class-based definitions @@ -621,7 +641,7 @@ class ModelRegistry: columns={ 'live': 'is_live', 'zIndex': 'z_index', - 'gisType': 'geom_type', + 'gisType': 'gis_type', # 'type': 'mapbox_type', 'viewableRole': 'viewable_role', }, inplace=True