Cosmetic: mostly typings

This commit is contained in:
phil 2024-02-13 12:47:07 +05:30
parent df5f67b79d
commit c1f229f805
13 changed files with 120 additions and 69 deletions

View file

@ -12,11 +12,14 @@ from gisaf.models.authentication import (
Role, RoleRead, Role, RoleRead,
) )
from gisaf.models.category import Category, CategoryRead from gisaf.models.category import Category, CategoryRead
from gisaf.models.geo_models_base import LineWorkSurveyModel
from gisaf.models.to_migrate import DataProvider from gisaf.models.to_migrate import DataProvider
from gisaf.config import conf from gisaf.models.survey import Equipment, SurveyMeta, Surveyor
from gisaf.config import Survey, conf
from gisaf.models.bootstrap import BootstrapData from gisaf.models.bootstrap import BootstrapData
from gisaf.models.store import Store from gisaf.models.store import Store, StoreNameOnly
from gisaf.models.project import Project from gisaf.models.project import Project
from gisaf.models.authentication import UserRoleLink #, ACL
from gisaf.database import pandas_query, fastapi_db_session as db_session from gisaf.database import pandas_query, fastapi_db_session as db_session
from gisaf.security import ( from gisaf.security import (
Token, Token,
@ -69,17 +72,26 @@ async def login_for_access_token(
async def get_users( async def get_users(
db_session: db_session, db_session: db_session,
) -> list[UserRead]: ) -> list[UserRead]:
query = select(User).options(selectinload(User.roles)) query = select(User).options(selectinload(User.roles)) # type: ignore[arg-type]
data = await db_session.exec(query) data = await db_session.exec(query)
return data.all() return data.all() # type: ignore[return-value]
@api.get("/roles") @api.get("/roles")
async def get_roles( async def get_roles(
db_session: db_session, db_session: db_session,
) -> list[RoleRead]: ) -> list[RoleRead]:
query = select(Role).options(selectinload(Role.users)) query = select(Role).options(selectinload(Role.users)) # type: ignore[arg-type]
data = await db_session.exec(query) data = await db_session.exec(query)
return data.all() return data.all() # type: ignore[return-value]
@api.get('/acls')
async def get_acls(db_session: db_session,
user: Annotated[User, Depends(get_current_user)]) -> list[UserRoleLink]:
"""New: ACLs returned as UserRoleLink"""
if not user or not user.has_role('manager'):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
data = await db_session.exec(select(UserRoleLink))
return data.all() # type: ignore[return-value]
@api.get("/categories") @api.get("/categories")
async def get_categories( async def get_categories(
@ -87,7 +99,7 @@ async def get_categories(
) -> list[CategoryRead]: ) -> list[CategoryRead]:
query = select(Category) query = select(Category)
data = await db_session.exec(query) data = await db_session.exec(query)
return data.all() return data.all() # type: ignore[return-value]
@api.get("/categories_pandas") @api.get("/categories_pandas")
async def get_categories_p( async def get_categories_p(
@ -95,7 +107,7 @@ async def get_categories_p(
) -> list[CategoryRead]: ) -> list[CategoryRead]:
query = select(Category) query = select(Category)
df = await db_session.run_sync(pandas_query, query) df = await db_session.run_sync(pandas_query, query)
return df.to_dict(orient="records") return df.to_dict(orient="records") # type: ignore[return-value]
# @api.get("/list") # @api.get("/list")
@api.get("/data-providers") @api.get("/data-providers")
@ -114,15 +126,31 @@ async def list_data_providers() -> list[DataProvider]:
async def get_stores() -> list[Store]: async def get_stores() -> list[Store]:
df = registry.stores.reset_index().\ df = registry.stores.reset_index().\
drop(columns=['model', 'raw_model', 'base_gis_type']) drop(columns=['model', 'raw_model', 'base_gis_type'])
return df.to_dict(orient="records") return df.to_dict(orient="records") # type: ignore[return-value]
@api.get("/projects") @api.get("/projects")
async def get_projects( async def get_projects(
db_session: db_session, db_session: db_session,
) -> list[Project]: ) -> list[Project]:
query = select(Project) query = select(Project)
df = await db_session.run_sync(pandas_query, query) data = await db_session.exec(query)
return df.to_dict(orient="records") return data.all() # type: ignore[return-value]
@api.get("/survey_meta")
async def get_survey_meta(
db_session: db_session,
) -> SurveyMeta:
return SurveyMeta(
projects=(await db_session.exec(select(Project))).all(), # type: ignore[arg-type]
surveyors=(await db_session.exec(select(Surveyor))).all(), # type: ignore[arg-type]
equipments=(await db_session.exec(select(Equipment))).all(), # type: ignore[arg-type]
statuses=conf.map.status,
stores_misc=[StoreNameOnly(name=name)
for name, model in registry.geom_custom.items()],
stores_line_work=[StoreNameOnly(name=name)
for name in registry.stores[registry.stores.is_line_work].index],
default=conf.admin.basket.default
)
@api.get("/feature-info/{store}/{id}") @api.get("/feature-info/{store}/{id}")
async def get_feature_info( async def get_feature_info(

View file

@ -6,7 +6,7 @@ from typing import Any, Type, Tuple
from pydantic_settings import (BaseSettings, from pydantic_settings import (BaseSettings,
PydanticBaseSettingsSource, PydanticBaseSettingsSource,
SettingsConfigDict) SettingsConfigDict)
from pydantic import ConfigDict #from pydantic import ConfigDict
from pydantic.v1.utils import deep_update from pydantic.v1.utils import deep_update
from yaml import safe_load from yaml import safe_load

View file

@ -1,10 +1,11 @@
from sqlmodel import Field, SQLModel, Relationship from sqlmodel import Field, SQLModel, Relationship
from pydantic import BaseModel
from gisaf.models.metadata import gisaf_admin from gisaf.models.metadata import gisaf_admin
class UserRoleLink(SQLModel, table=True): class UserRoleLink(SQLModel, table=True):
__tablename__ = 'roles_users' __tablename__: str = 'roles_users' # type: ignore
__table_args__ = gisaf_admin.table_args __table_args__ = gisaf_admin.table_args
user_id: int | None = Field( user_id: int | None = Field(
default=None, default=None,
@ -56,7 +57,7 @@ class Role(RoleWithDescription, table=True):
class UserReadNoRoles(UserBase): class UserReadNoRoles(UserBase):
id: int id: int
email: str | None email: str | None # type: ignore
class RoleRead(RoleBase): class RoleRead(RoleBase):
@ -70,5 +71,10 @@ class RoleReadNoUsers(RoleBase):
class UserRead(UserBase): class UserRead(UserBase):
id: int id: int
email: str | None email: str | None # type: ignore
roles: list[RoleReadNoUsers] = [] roles: list[RoleReadNoUsers] = []
# class ACL(BaseModel):
# user_id: int
# role_ids: list[int]

View file

@ -16,4 +16,4 @@ class BootstrapData(BaseModel):
geo: Geo = conf.geo geo: Geo = conf.geo
measures: Measures = conf.measures measures: Measures = conf.measures
redirect: str = conf.gisaf.redirect redirect: str = conf.gisaf.redirect
user: UserRead | None = None user: UserRead | None = None # type: ignore

View file

@ -15,9 +15,9 @@ mapbox_type_mapping = {
class CategoryGroup(BaseModel, table=True): class CategoryGroup(BaseModel, table=True):
__tablename__ = 'category_group' __tablename__: str = 'category_group' # type: ignore
__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) # type: ignore
major: bool major: bool
long_name: str long_name: str
categories: list['Category'] = Relationship(back_populates='category_group') categories: list['Category'] = Relationship(back_populates='category_group')
@ -28,7 +28,7 @@ class CategoryGroup(BaseModel, table=True):
class CategoryModelType(BaseModel, table=True): class CategoryModelType(BaseModel, table=True):
__tablename__ = 'category_model_type' __tablename__: str = 'category_model_type' # type: ignore
__table_args__ = gisaf_survey.table_args __table_args__ = gisaf_survey.table_args
name: str | None = Field(default=None, primary_key=True) name: str | None = Field(default=None, primary_key=True)
@ -46,27 +46,27 @@ class CategoryBase(BaseModel):
name: str | None = Field(default=None, primary_key=True) name: str | None = Field(default=None, primary_key=True)
domain: ClassVar[str] = 'V' domain: ClassVar[str] = 'V'
description: str | None description: str | None
group: str = Field(sa_type=String(4), group: str = Field(sa_type=String(4), # type: ignore
foreign_key=gisaf_survey.table('category_group.name'), foreign_key=gisaf_survey.table('category_group.name'), # type: ignore
index=True) index=True) # type: ignore
minor_group_1: str = Field(sa_type=String(4), default='----') minor_group_1: str = Field(sa_type=String(4), default='----') # type: ignore
minor_group_2: str = Field(sa_type=String(4), default='----') minor_group_2: str = Field(sa_type=String(4), default='----') # type: ignore
status: str = Field(sa_type=String(1)) status: str = Field(sa_type=String(1)) # type: ignore
custom: bool | None custom: bool | None
auto_import: bool = True auto_import: bool = True
gis_type: str = Field(sa_type=String(50), gis_type: str = Field(sa_type=String(50), # type: ignore
foreign_key=gisaf_survey.table('category_model_type.name'), foreign_key=gisaf_survey.table('category_model_type.name'), # type: ignore
default='Point') default='Point') # type: ignore
long_name: str | None = Field(sa_type=String(50)) long_name: str | None = Field(sa_type=String(50)) # type: ignore
style: str | None = Field(sa_type=TEXT) style: str | None = Field(sa_type=TEXT)
symbol: str | None = Field(sa_type=String(1)) symbol: str | None = Field(sa_type=String(1)) # type: ignore
mapbox_type_custom: str | None = Field(sa_type=String(12)) mapbox_type_custom: str | None = Field(sa_type=String(12)) # type: ignore
mapbox_paint: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) mapbox_paint: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) # type: ignore
mapbox_layout: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) mapbox_layout: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) # type: ignore
viewable_role: str | None viewable_role: str | None
extra: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) extra: dict[str, Any] | None = Field(sa_type=JSON(none_as_null=True)) # type: ignore
@computed_field @computed_field # type: ignore
@property @property
def layer_name(self) -> str: def layer_name(self) -> str:
""" """
@ -75,7 +75,7 @@ class CategoryBase(BaseModel):
""" """
return '{self.domain}-{self.group:4s}-{self.minor_group_1:4s}-{self.minor_group_2:4s}-{self.status:1s}'.format(self=self) return '{self.domain}-{self.group:4s}-{self.minor_group_1:4s}-{self.minor_group_2:4s}-{self.status:1s}'.format(self=self)
@computed_field @computed_field # type: ignore
@property @property
def table_name(self) -> str: def table_name(self) -> str:
""" """
@ -87,7 +87,7 @@ class CategoryBase(BaseModel):
else: else:
return '{self.domain}_{self.group:4s}_{self.minor_group_1:4s}_{self.minor_group_2:4s}'.format(self=self) return '{self.domain}_{self.group:4s}_{self.minor_group_1:4s}_{self.minor_group_2:4s}'.format(self=self)
@computed_field @computed_field # type: ignore
@property @property
def raw_survey_table_name(self) -> str: def raw_survey_table_name(self) -> str:
""" """
@ -99,7 +99,7 @@ class CategoryBase(BaseModel):
else: else:
return 'RAW_{self.domain}_{self.group:4s}_{self.minor_group_1:4s}_{self.minor_group_2:4s}'.format(self=self) return 'RAW_{self.domain}_{self.group:4s}_{self.minor_group_1:4s}_{self.minor_group_2:4s}'.format(self=self)
@computed_field @computed_field # type: ignore
@property @property
def mapbox_type(self) -> str: def mapbox_type(self) -> str:
return self.mapbox_type_custom or mapbox_type_mapping[self.gis_type] return self.mapbox_type_custom or mapbox_type_mapping[self.gis_type]

View file

@ -18,11 +18,11 @@ logger = logging.getLogger(__name__)
try: try:
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
except ImportError: except ImportError:
plt = None plt = None # type: ignore
class DashboardPageSource(Model, table=True): class DashboardPageSource(Model, table=True):
__tablename__ = 'dashboard_page_source' __tablename__ = 'dashboard_page_source' # type: ignore
__table_args__ = gisaf.table_args __table_args__ = gisaf.table_args
id: str = Field(primary_key=True) id: str = Field(primary_key=True)
@ -118,7 +118,7 @@ class DashboardPageCommon:
class DashboardPage(Model, DashboardPageCommon, table=True): class DashboardPage(Model, DashboardPageCommon, table=True):
__tablename__ = 'dashboard_page' __tablename__ = 'dashboard_page' # type: ignore
__table_args__ = gisaf.table_args __table_args__ = gisaf.table_args
class Admin: class Admin:
@ -179,7 +179,7 @@ class DashboardPage(Model, DashboardPageCommon, table=True):
class DashboardPageSection(Model, DashboardPageCommon, table=True): class DashboardPageSection(Model, DashboardPageCommon, table=True):
__tablename__ = 'dashboard_page_section' __tablename__ = 'dashboard_page_section' # type: ignore
__table_args__ = gisaf.table_args __table_args__ = gisaf.table_args
class Admin: class Admin:
@ -252,10 +252,10 @@ class DashboardPageSection(Model, DashboardPageCommon, table=True):
class Widget(Model, table=True): class Widget(Model, table=True):
__tablename__ = 'widget' __tablename__ = 'widget' # type: ignore
__table_args__ = gisaf.table_args __table_args__ = gisaf.table_args
## CREATE TABLE gisaf.widget (name char(50) not null PRIMARY KEY, title varchar, subtitle varchar, notebook varchar, content varchar, time timestamp); ## CREATE TABLE gisaf.widget (name char(50) not null PRIMARY KEY, title varchar, subtitle varchar, notebook varchar, content varchar, time timestamp);
name: str = Field(primary_key=True, sa_type=String(50)) name: str = Field(primary_key=True, sa_type=String(50)) # type: ignore
title: str title: str
subtitle: str subtitle: str
content: str content: str

View file

@ -91,10 +91,10 @@ class BaseSurveyModel(BaseModel):
@classmethod @classmethod
def selectinload(cls): def selectinload(cls):
return [ return [
cls.equipment, cls.equipment, # type: ignore
cls.surveyor, cls.surveyor, # type: ignore
cls.accuracy, cls.accuracy, # type: ignore
cls.project, cls.project, # type: ignore
] ]
# @classmethod # @classmethod
# def dyn_join_with(cls): # def dyn_join_with(cls):

View file

@ -6,7 +6,12 @@ from gisaf.models.geo_models_base import GeoModel, RawSurveyBaseModel, GeoPointS
class MapLibreStyle(BaseModel): class MapLibreStyle(BaseModel):
... ...
class Store(BaseModel):
class StoreNameOnly(BaseModel):
name: str
class Store(StoreNameOnly):
auto_import: bool auto_import: bool
# base_gis_type: str # base_gis_type: str
count: int | None = None count: int | None = None
@ -29,7 +34,6 @@ class Store(BaseModel):
minor_group_2: str | None minor_group_2: str | None
#model: GeoModel #model: GeoModel
gis_type: str gis_type: str
name: str
#name_letter: str #name_letter: str
#name_number: int #name_number: int
#raw_model: GeoPointSurveyModel #raw_model: GeoPointSurveyModel

View file

@ -1,9 +1,13 @@
from enum import Enum from enum import Enum
from sqlmodel import Field, Relationship from sqlmodel import Field, Relationship
from pydantic import BaseModel
from gisaf.config import BasketDefault
from gisaf.models.models_base import Model from gisaf.models.models_base import Model
from gisaf.models.metadata import gisaf_survey from gisaf.models.metadata import gisaf_survey
from gisaf.models.project import Project
from gisaf.models.store import StoreNameOnly
class Accuracy(Model, table=True): class Accuracy(Model, table=True):
@ -63,7 +67,7 @@ class GeometryType(str, Enum):
class AccuracyEquimentSurveyorMapping(Model, table=True): class AccuracyEquimentSurveyorMapping(Model, table=True):
__table_args__ = gisaf_survey.table_args __table_args__ = gisaf_survey.table_args
__tablename__ = 'accuracy_equiment_surveyor_mapping' __tablename__: str = 'accuracy_equiment_surveyor_mapping' # type: ignore
class Admin: class Admin:
menu = 'Other' menu = 'Other'
@ -91,4 +95,13 @@ class AccuracyEquimentSurveyorMapping(Model, table=True):
# 'surveyor': Surveyor, # 'surveyor': Surveyor,
# 'equipment': Equipment, # 'equipment': Equipment,
# 'accuracy': Accuracy, # 'accuracy': Accuracy,
# } # }
class SurveyMeta(BaseModel):
projects: list[Project]
surveyors: list[Surveyor]
equipments: list[Equipment]
statuses: list[str]
stores_misc: list[StoreNameOnly]
stores_line_work: list[StoreNameOnly]
default: BasketDefault

View file

@ -19,7 +19,7 @@ class Tags(GeoPointModel, table=True):
id: int | None = Field(primary_key=True, default=None) id: int | None = Field(primary_key=True, default=None)
store: str = Field(index=True) store: str = Field(index=True)
ref_id: int = Field(index=True, sa_type=BigInteger) ref_id: int = Field(index=True, sa_type=BigInteger)
tags: dict = Field(sa_type=MutableDict.as_mutable(HSTORE)) tags: dict = Field(sa_type=MutableDict.as_mutable(HSTORE)) # type: ignore
def __str__(self): def __str__(self):
return '{self.store:s} {self.ref_id}: {self.tags}'.format(self=self) return '{self.store:s} {self.ref_id}: {self.tags}'.format(self=self)

View file

@ -7,7 +7,7 @@ from time import time
import logging import logging
import pandas as pd import pandas as pd
import geopandas as gpd import geopandas as gpd # type: ignore[import-untyped]
from asyncpg import connect from asyncpg import connect
from asyncpg.connection import Connection from asyncpg.connection import Connection
from asyncpg.exceptions import UndefinedTableError, InterfaceError from asyncpg.exceptions import UndefinedTableError, InterfaceError
@ -167,8 +167,8 @@ class Store:
Additionally, publish to the channel for websocket live updates to ws_clients Additionally, publish to the channel for websocket live updates to ws_clients
""" """
if gdf is None: if gdf is None:
gdf = gpd.GeoDataFrame(data={'geom': []}, geometry='geom') gdf = gpd.GeoDataFrame(data={'geom': []}, geometry='geom') # type: ignore
if isinstance(gdf.index, pd.core.indexes.multi.MultiIndex): if isinstance(gdf.index, pd.MultiIndex):
raise ValueError('Gisaf live does not accept dataframes with multi index') raise ValueError('Gisaf live does not accept dataframes with multi index')
return await self._store_live_to_redis(live_name, gdf, **kwargs) return await self._store_live_to_redis(live_name, gdf, **kwargs)
@ -205,7 +205,7 @@ class Store:
mapbox_layout['text-field'] = symbol mapbox_layout['text-field'] = symbol
if not symbol: if not symbol:
symbol = gisTypeSymbolMap.get(gis_type, '\ue02e') symbol = gisTypeSymbolMap.get(gis_type, '\ue02e')
if properties == None: if properties is None:
properties = [] properties = []
## Add a column for json representation ## Add a column for json representation
columns = {'status', 'popup', gdf.geometry.name, 'store', 'id'} columns = {'status', 'popup', gdf.geometry.name, 'store', 'id'}

View file

@ -108,7 +108,7 @@ def verify_password(user: User, plain_password):
async def get_current_user( async def get_current_user(
token: str = Depends(oauth2_scheme)) -> UserRead | None: token: str = Depends(oauth2_scheme)) -> User | None:
if token is None: if token is None:
return None return None
try: try:

View file

@ -1,11 +1,11 @@
import logging import logging
import asyncio import asyncio
from functools import wraps from functools import wraps
from json import dumps, JSONEncoder from json import JSONEncoder
from math import isnan from math import isnan
from time import time from time import time
import datetime import datetime
import pyproj from typing import Any
from numpy import ndarray from numpy import ndarray
import pandas as pd import pandas as pd
@ -27,7 +27,7 @@ SHAPELY_TYPE_TO_MAPBOX_TYPE = {
'MultiPolygon': 'fill', 'MultiPolygon': 'fill',
} }
DEFAULT_MAPBOX_LAYOUT = { DEFAULT_MAPBOX_LAYOUT: dict[str, dict[str, Any]] = {
'symbol': { 'symbol': {
'text-line-height': 1, 'text-line-height': 1,
'text-padding': 0, 'text-padding': 0,
@ -153,13 +153,13 @@ class NumpyEncoder(JSONEncoder):
# recursive_joins[name] = join # recursive_joins[name] = join
# return recursive_joins # return recursive_joins
def get_joined_query(cls): # def get_joined_query(cls):
""" # """
Helper function to get a query from a model with all the related tables loaded # Helper function to get a query from a model with all the related tables loaded
:param cls: # :param cls:
:return: # :return:
""" # """
return cls.load(**get_join_with(cls)).query # return cls.load(**get_join_with(cls)).query
def timeit(f): def timeit(f):