Implement map api (ex. /v2/map/init-data)
Migrate registry.primary_groups to pydantic model
This commit is contained in:
parent
e43c88d0ab
commit
7e9e266157
6 changed files with 116 additions and 36 deletions
62
src/gisaf/api/map.py
Normal file
62
src/gisaf/api/map.py
Normal 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')
|
||||
)
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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}"
|
||||
return f"{self.store or '':s}"
|
||||
|
||||
|
||||
class MapInitData(BaseModel):
|
||||
baseStyles: list[BaseStyle] = []
|
||||
baseMaps: list[BaseMap] = []
|
||||
groups: list[CategoryGroup] = []
|
||||
stores: list[Store] = []
|
|
@ -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
|
||||
|
|
|
@ -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, '
|
||||
'eventually as the surveys get more complete)',
|
||||
'Misc',
|
||||
)
|
||||
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)',
|
||||
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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue