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.api.dashboard import api as dashboard_api
from gisaf.api.map import api as map_api
logger = logging.getLogger(__name__)
@ -38,6 +39,7 @@ api = FastAPI(
)
#api.add_middleware(SessionMiddleware, secret_key=conf.crypto.secret)
api.mount('/dashboard', dashboard_api)
api.mount('/map', map_api)
@api.get('/bootstrap')

View file

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

View file

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

View file

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