Implement map api (ex. /v2/map/init-data)

Migrate registry.primary_groups to pydantic model
This commit is contained in:
phil 2024-01-11 12:55:42 +05:30
parent e43c88d0ab
commit 7e9e266157
6 changed files with 116 additions and 36 deletions

62
src/gisaf/api/map.py Normal file
View file

@ -0,0 +1,62 @@
from collections import OrderedDict, defaultdict
import logging
from pathlib import Path
from fastapi import Depends, FastAPI, HTTPException, status, responses
from sqlalchemy.orm import selectinload
from sqlmodel import select
from gisaf.config import conf
from gisaf.models.map_bases import BaseMap, BaseMapLayer, BaseStyle, MapInitData
from gisaf.registry import registry
from gisaf.database import db_session
from gisaf.models.authentication import User
logger = logging.getLogger(__name__)
api = FastAPI(
default_response_class=responses.ORJSONResponse,
)
async def get_base_styles():
async with db_session() as session:
query = select(BaseStyle.name)\
.where(BaseStyle.enabled==True)\
.order_by(BaseStyle.id)
data = await session.exec(query)
base_styles = data.all()
## TODO: tiles_registry
logger.warning('TODO: tiles_registry')
# base_styles.extend(tiles_registry.mbtiles.values())
return [BaseStyle(name=bs) for bs in base_styles]
async def get_base_maps() -> list[BaseMap]:
async with db_session() as session:
query1 = select(BaseMap).options(selectinload(BaseMap.layers))
data1 = await session.exec(query1)
base_maps = data1.all()
return base_maps
base_map_dict = {bm.id: bm.name for bm in base_maps}
query2 = select(BaseMapLayer).options(selectinload(BaseMapLayer.base_map))
data2 = await session.exec(query2)
base_map_layers = data2.all()
bms = defaultdict(list)
for bml in base_map_layers:
breakpoint()
if bml.store:
bms[base_map_dict[bml.base_map_id]].append(name=bml.store)
return [
BaseMap(name=bm, stores=bmls)
for bm, bmls in OrderedDict(sorted(bms.items())).items()
]
@api.get('/init-data')
async def get_init_data() -> MapInitData:
await registry.update_stores_counts()
df = registry.stores.reset_index().\
drop(columns=['model', 'raw_model', 'base_gis_type'])
return MapInitData(
baseStyles=await get_base_styles(),
baseMaps=await get_base_maps(),
groups=registry.primary_groups,
stores=df.to_dict(orient='records')
)

View file

@ -29,6 +29,7 @@ from gisaf.models.to_migrate import (
) )
from gisaf.live_utils import get_live_feature_info from gisaf.live_utils import get_live_feature_info
from gisaf.api.dashboard import api as dashboard_api from gisaf.api.dashboard import api as dashboard_api
from gisaf.api.map import api as map_api
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -38,6 +39,7 @@ api = FastAPI(
) )
#api.add_middleware(SessionMiddleware, secret_key=conf.crypto.secret) #api.add_middleware(SessionMiddleware, secret_key=conf.crypto.secret)
api.mount('/dashboard', dashboard_api) api.mount('/dashboard', dashboard_api)
api.mount('/map', map_api)
@api.get('/bootstrap') @api.get('/bootstrap')

View file

@ -18,7 +18,7 @@ class CategoryGroup(BaseModel, table=True):
__tablename__ = 'category_group' __tablename__ = 'category_group'
__table_args__ = gisaf_survey.table_args __table_args__ = gisaf_survey.table_args
name: str | None = Field(sa_type=String(4), default=None, primary_key=True) name: str | None = Field(sa_type=String(4), default=None, primary_key=True)
major: str major: bool
long_name: str long_name: str
categories: list['Category'] = Relationship(back_populates='category_group') categories: list['Category'] = Relationship(back_populates='category_group')

View file

@ -1,12 +1,15 @@
from typing import Any from typing import Any
from pydantic import BaseModel
from sqlmodel import Field, String, JSON, Relationship from sqlmodel import Field, String, JSON, Relationship
from gisaf.models.models_base import Model from gisaf.models.models_base import Model
from gisaf.models.category import CategoryGroup
from gisaf.models.metadata import gisaf_map from gisaf.models.metadata import gisaf_map
from gisaf.models.store import Store
class BaseStyle(Model): class BaseStyle(Model, table=True):
__table_args__ = gisaf_map.table_args __table_args__ = gisaf_map.table_args
__tablename__ = 'map_base_style' __tablename__ = 'map_base_style'
@ -25,7 +28,7 @@ class BaseStyle(Model):
return '<models.BaseStyle {self.name:s}>'.format(self=self) return '<models.BaseStyle {self.name:s}>'.format(self=self)
class BaseMap(Model): class BaseMap(Model, table=True):
__table_args__ = gisaf_map.table_args __table_args__ = gisaf_map.table_args
__tablename__ = 'base_map' __tablename__ = 'base_map'
@ -34,6 +37,7 @@ class BaseMap(Model):
id: int | None = Field(primary_key=True, default=None) id: int | None = Field(primary_key=True, default=None)
name: str name: str
layers: list['BaseMapLayer'] = Relationship(back_populates='base_map')
def __repr__(self): def __repr__(self):
return '<models.BaseMap {self.name:s}>'.format(self=self) return '<models.BaseMap {self.name:s}>'.format(self=self)
@ -41,8 +45,14 @@ class BaseMap(Model):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def selectinload(cls):
return [
cls.layers
]
class BaseMapLayer(Model):
class BaseMapLayer(Model, table=True):
__table_args__ = gisaf_map.table_args __table_args__ = gisaf_map.table_args
__tablename__ = 'base_map_layer' __tablename__ = 'base_map_layer'
@ -50,9 +60,9 @@ class BaseMapLayer(Model):
menu = 'Other' menu = 'Other'
id: int | None = Field(primary_key=True, default=None) id: int | None = Field(primary_key=True, default=None)
base_map_id: int = Field(foreign_key='base_map.id', index=True) base_map_id: int = Field(foreign_key=gisaf_map.table('base_map.id'),
## Subclasses must include: index=True)
# base_map: BaseMap = Relationship() base_map: BaseMap = Relationship(back_populates='layers')
store: str = Field(sa_type=String(100)) store: str = Field(sa_type=String(100))
@classmethod @classmethod
@ -61,14 +71,15 @@ class BaseMapLayer(Model):
cls.base_map cls.base_map
] ]
# @classmethod
# def dyn_join_with(cls):
# return {
# 'base_map': BaseMap,
# }
def __repr__(self): def __repr__(self):
return f"<models.BaseMapLayer {self.store or '':s}>" return f"<models.BaseMapLayer {self.store or '':s}>"
def __str__(self): def __str__(self):
return f"{self.store or '':s}" return f"{self.store or '':s}"
class MapInitData(BaseModel):
baseStyles: list[BaseStyle] = []
baseMaps: list[BaseMap] = []
groups: list[CategoryGroup] = []
stores: list[Store] = []

View file

@ -9,7 +9,7 @@ class MapLibreStyle(BaseModel):
class Store(BaseModel): class Store(BaseModel):
auto_import: bool auto_import: bool
# base_gis_type: str # base_gis_type: str
count: int count: int | None = None
custom: bool custom: bool
description: str description: str
#extra: dict[str, Any] | None #extra: dict[str, Any] | None

View file

@ -77,6 +77,7 @@ class ModelRegistry:
""" """
stores: pd.DataFrame stores: pd.DataFrame
categories: pd.DataFrame categories: pd.DataFrame
primary_groups: list[CategoryGroup]
values: dict[str, PlottableModel] values: dict[str, PlottableModel]
geom: dict[str, GeoModel] geom: dict[str, GeoModel]
geom_live: dict[str, LiveGeoModel] geom_live: dict[str, LiveGeoModel]
@ -567,31 +568,35 @@ class ModelRegistry:
self.stores['description'].fillna('', inplace=True) self.stores['description'].fillna('', inplace=True)
## Layer groups: Misc, survey's primary groups, Live ## Layer groups: Misc, survey's primary groups, Live
self.primary_groups = await CategoryGroup.get_df() async with db_session() as session:
self.primary_groups.sort_values('name', inplace=True) query = select(CategoryGroup).order_by(CategoryGroup.name)
self.primary_groups['title'] = self.primary_groups['long_name'] data = await session.exec(query)
self.primary_groups = data.all()
## Add Misc and Live ## Add Misc and Live
self.primary_groups.loc['Misc'] = ( self.primary_groups.append(CategoryGroup(
False, name='Misc',
'Misc and old layers (not coming from our survey; they will be organized, ' major=True,
'eventually as the surveys get more complete)', long_name='Misc and old layers (not coming from our survey; '
'Misc', 'they will be organized, '
) 'eventually as the surveys get more complete)',
categories=[],
))
self.primary_groups.loc['Live'] = ( self.primary_groups.append(CategoryGroup(
False, name='Live',
'Layers from data processing, sensors, etc, and are updated automatically', major=True,
'Live', long_name='Layers from data processing, sensors, etc, '
) 'and are updated automatically',
categories=[],
))
self.primary_groups.loc['Community'] = ( self.primary_groups.append(CategoryGroup(
False, name='Community',
'Layers from community', major=True,
'Community', long_name='Layers from community',
) categories=[],
))
self.primary_groups.sort_index(inplace=True)
#def make_group(group): #def make_group(group):
# return GeomGroup( # return GeomGroup(