pr0nstar

V

......@@ -14,7 +14,8 @@ from aiocache import caches
from presupuestov1.routers import (
project,
entity
entity,
classifier
)
......@@ -40,6 +41,10 @@ app = FastAPI(
)
app.include_router(project.router)
app.include_router(entity.router)
app.include_router(classifier.router_objeto)
app.include_router(classifier.router_acteco)
app.include_router(classifier.router_finfun)
@app.on_event('startup')
......
......@@ -72,3 +72,31 @@ class TreemapActeco(Base):
desc_acteco: Mapped[str | None] = mapped_column(Text)
parent: Mapped[str | None] = mapped_column(String(9))
devengado: Mapped[float | None] = mapped_column(Numeric(30, 15))
###############################################################################
# fns
###############################################################################
def get_entity_connections(entity_id, db, query, ClassObject, object_id):
query_filters = [
ClassObject.entidad == entity_id,
]
return get_clas_entities(query_filters, db, query, ClassObject, object_id)
def get_clas_entities(query_filters, db, query, ClassObject, object_id):
if query.gestion is None:
query.gestion = 0
query_filters.append(ClassObject.gestion == query.gestion)
if query.id is not None:
ids = query.id.split(',')
ids = [int(_.strip()) for _ in ids]
ids = or_([getattr(ClassObject, object_id) == _ for _ in ids])
query_filters.append(ids)
return db.query(ClassObject).filter(and_(*query_filters)).all()
......
fastapi>=0.110,<1.0
uvicorn[standard]>=0.29,<1.0
sqlalchemy>=2.0,<3.0
pydantic>=2.6,<3.0
starlette-context>=0.3,<1.0
aiocache>=0.12,<1.0
numpy>=1.26,<3.0
typesense>=0.21,<1.0
psycopg2-binary>=2.9,<3.0
import time
from typing import List, Any, Optional, Union
from fastapi import APIRouter, Depends, Request, HTTPException, Body
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_
from presupuestov1 import model, schema, constant, schemas, models, search
from aiocache import cached
router_objeto = APIRouter(
prefix='/objeto',
tags=['objeto'],
)
router_acteco = APIRouter(
prefix='/acteco',
tags=['acteco'],
)
router_finfun = APIRouter(
prefix='/finfun',
tags=['finfun'],
)
###############################################################################
# GET
###############################################################################
@router_objeto.get('/clasificador', response_model=List[schemas.classifier.ClasObjetos])
def fetch_object_classifier(db: Session = Depends(model.get_db)):
return _fetch_classifier('objeto', db)
@router_acteco.get('/clasificador', response_model=List[schemas.classifier.ClasActeco])
def fetch_object_classifier(db: Session = Depends(model.get_db)):
return _fetch_classifier('acteco', db)
@router_finfun.get('/clasificador', response_model=List[schemas.classifier.ClasFinfun])
def fetch_object_classifier(db: Session = Depends(model.get_db)):
return _fetch_classifier('finfun', db)
@router_objeto.get('/{object_id}', response_model=schemas.classifier.ClasObjetos)
def fetch_clas(
object_id: str,
db: Session = Depends(model.get_db),
):
ClasObjetos = models.classifier.ClasObjetos
return db.query(ClasObjetos).filter(
ClasObjetos.objeto == '{}.0'.format(object_id)
).one()
@router_objeto.get('/{object_id}/entidades', response_model=List[schemas.treemap.TreemapObjeto])
def fetch_clas(
object_id: str,
db: Session = Depends(model.get_db),
):
Treemap = models.treemap.TreemapObjeto
return models.treemap.get_clas_entities(
[
Treemap.objeto == object_id
], db, query, Treemap, 'entidad'
)
@router_acteco.get('/{acteco_id}', response_model=schemas.classifier.ClasActeco)
def fetch_clas(
acteco_id: str,
db: Session = Depends(model.get_db),
):
ClasActeco = models.classifier.ClasActeco
return db.query(ClasActeco).filter(
ClasActeco.acteco == acteco_id
).one()
@router_acteco.get('/{acteco_id}/entidades', response_model=List[schemas.treemap.TreemapActeco])
def fetch_clas(
acteco_id: str,
db: Session = Depends(model.get_db),
):
Treemap = models.treemap.TreemapActeco
return models.treemap.get_clas_entities(
[
Treemap.acteco == acteco_id
], db, query, Treemap, 'entidad'
)
@router_finfun.get('/{finfun_id}', response_model=schemas.classifier.ClasFinfun)
def fetch_clas(
finfun_id: str,
db: Session = Depends(model.get_db),
):
ClasFinfun = models.classifier.ClasFinfun
return db.query(ClasFinfun).filter(
ClasFinfun.finfun == finfun_id
).one()
@router_finfun.get('/{finfun_id}/entidades', response_model=List[schemas.treemap.TreemapFinFun])
def fetch_clas(
finfun_id: str,
db: Session = Depends(model.get_db),
):
Treemap = models.treemap.TreemapFinFun
return models.treemap.get_clas_entities(
[
Treemap.finfun == finfun_id
], db, query, Treemap, 'entidad'
)
###############################################################################
# priv
###############################################################################
def _fetch_classifier(clas, db):
ClassObject = {
'objeto': models.classifier.ClasObjetos,
'acteco': models.classifier.ClasActeco,
'finfun': models.classifier.ClasFinfun,
}[clas]
return db.query(ClassObject).all()
......@@ -46,7 +46,9 @@ def fetch_entity_objetos(
query: schema.QueryParams = Depends(),
):
TreemapObjeto = models.treemap.TreemapObjeto
return get_entity_connections(entity_id, db, query, TreemapObjeto, 'objeto')
return models.treemap.get_entity_connections(
entity_id, db, query, TreemapObjeto, 'objeto'
)
router.get('/{entity_id:int}/finfuns', response_model=List[schemas.treemap.TreemapFinFun])
......@@ -56,7 +58,9 @@ def fetch_entity_finfuns(
query: schema.QueryParams = Depends(),
):
TreemapFinFun = models.treemap.TreemapFinFun
return get_entity_connections(entity_id, db, query, TreemapFinFun, 'finfun')
return models.treemap.get_entity_connections(
entity_id, db, query, TreemapFinFun, 'finfun'
)
@router.get('/{entity_id:int}/actecos', response_model=List[schemas.treemap.TreemapActeco])
......@@ -66,29 +70,7 @@ def fetch_entity_actecos(
query: schema.QueryParams = Depends(),
):
TreemapActeco = models.treemap.TreemapActeco
return get_entity_connections(entity_id, db, query, TreemapActeco, 'acteco')
return models.treemap.get_entity_connections(
entity_id, db, query, TreemapActeco, 'acteco'
)
###############################################################################
# Private
###############################################################################
def get_entity_connections(entity_id, db, query, ClassObject, object_id):
if query.gestion is None:
query.gestion = 0
query_filters = [
ClassObject.entidad == entity_id,
ClassObject.gestion == query.gestion,
]
print(query.id is not None)
if query.id is not None:
ids = query.id.split(',')
ids = [int(_.strip()) for _ in ids]
ids = or_([getattr(ClassObject, object_id) == _ for _ in ids])
query_filters.append(ids)
return db.query(ClassObject).filter(and_(*query_filters)).all()
\ No newline at end of file
......
......@@ -34,15 +34,23 @@ def fetch_search(
query: schema.SearchQueryParams = Depends(),
):
if query.q is None or len(query.q.strip()) < 3:
return []
return {
'facet_counts': [],
'found': 0,
'out_of': 0,
'page': 0,
'search_time_ms': 0,
'hits': [],
}
return do_search(query.q, query.page, query.is_class)
return search.do_search(
query.q, query.page, query.is_class, query.class_, query.order
)
###############################################################################
# GET
###############################################################################
# @cached(ttl=10)
def do_search(query, page, is_class):
return search.do_search(query, page, is_class)
\ No newline at end of file
def do_search(query, page, is_class, class_):
return search.do_search(query, page, is_class, class_)
......
......@@ -11,8 +11,10 @@ class OrmBaseModel(BaseModel):
class SearchQueryParams(BaseModel):
page: Optional[int] = Query(default=1)
q: Optional[str] = Query(default=None)
q: Optional[str] = Query(default=None, max_length=128)
order: Optional[str] = Query(default=None, max_length=5)
is_class: Optional[bool] = Query(default=None)
class_: Optional[str] = Query(default=None)
class QueryParams(BaseModel):
id: Optional[str] = Query(default=None)
......
from typing import Any
from presupuestov1.schema import OrmBaseModel
from pydantic import create_model
class ClasInstitucional(OrmBaseModel):
......@@ -28,6 +29,13 @@ class ClasInstitucional(OrmBaseModel):
n_gestiones: int | None
def get_clasdesc(name):
return create_model(
'_ClasDesc{}'.format(name),
**{'gestion': str, 'desc_{}'.format(name): str},
)
class ClasObjetos(OrmBaseModel):
nivel: str | None
objeto: str
......@@ -35,7 +43,7 @@ class ClasObjetos(OrmBaseModel):
gestiones: str | None
n_gestiones: int | None
n_variaciones: int | None
descripciones: list[dict[str, Any]] | None
descripciones: list[get_clasdesc('objeto')] | None
class ClasRubros(OrmBaseModel):
......@@ -45,7 +53,7 @@ class ClasRubros(OrmBaseModel):
gestiones: str | None
n_gestiones: int | None
n_variaciones: int | None
descripciones: list[dict[str, Any]] | None
descripciones: list[get_clasdesc('rubro')] | None
class ClasFinfun(OrmBaseModel):
......@@ -55,4 +63,14 @@ class ClasFinfun(OrmBaseModel):
gestiones: str | None
n_gestiones: int | None
n_variaciones: int | None
descripciones: list[dict[str, Any]] | None
descripciones: list[get_clasdesc('finfun')] | None
class ClasActeco(OrmBaseModel):
nivel: str | None
acteco: str
desc_acteco: str | None
gestiones: str | None
n_gestiones: int | None
n_variaciones: int | None
descripciones: list[get_clasdesc('acteco')] | None
......
......@@ -7,6 +7,7 @@ class ProjectSearch(BaseModel):
gestion: int
devengado: float
is_class: bool
class_: str
texto: str
metadatos: str
......
import numpy as np
from presupuestov1.schema import OrmBaseModel
from pydantic import field_serializer
class TreemapObjeto(OrmBaseModel):
class Treemap(OrmBaseModel):
gestion: int
entidad: int
objeto: int
nivel: str | None
desc_objeto: str | None
parent: int | None
devengado: float | None
@field_serializer('devengado')
def do_round(self, v):
if v is not None:
return np.round(v, 2)
class TreemapFinFun(OrmBaseModel):
gestion: int
entidad: int
finfun: int
nivel: str | None
class TreemapObjeto(Treemap):
objeto: str
desc_objeto: str | None
class TreemapFinFun(Treemap):
finfun: str
desc_finfun: str | None
parent: int | None
devengado: float | None
class TreemapActeco(OrmBaseModel):
gestion: int
entidad: int
acteco: int
nivel: str | None
class TreemapActeco(Treemap):
acteco: str
desc_actividad: str | None
parent: int | None
devengado: float | None
......
......@@ -18,6 +18,7 @@ schema = {
{'name': 'gestion', 'type': 'int32', 'facet': True},
{'name': 'devengado', 'type': 'float'},
{'name': 'is_class', 'type': 'bool'},
{'name': 'class_', 'type': 'string'},
{'name': 'texto', 'type': 'string'},
{'name': 'metadatos', 'type': 'string'},
],
......@@ -33,8 +34,10 @@ search_parameters = {
}
def do_search(query, page, is_class):
AVAILABLE_CLASSES = [
'programa_proyecto', 'entidad', 'objeto', 'finfun', 'acteco'
]
def do_search(query, page, is_class, class_=None, order=None):
search_parameters_ = search_parameters.copy()
search_parameters_['q'] = query
......@@ -47,9 +50,18 @@ def do_search(query, page, is_class):
search_parameters_['filter_by'] = filter_c
if (page is not None) and (page > 0):
if (class_ is not None):
class_ = [_ for _ in class_.split(',') if _ in AVAILABLE_CLASSES]
if len(class_):
search_parameters_['filter_by'] = 'class_:[{}]'.format(', '.join(class_))
if (page is not None) and (page > 0) and (page < 100):
search_parameters_['page'] = page
search_parameters_['sort_by'] = 'devengado:desc'
if order == 'asc':
search_parameters_['sort_by'] = 'devengado:asc'
return client.collections[COLLECTION].documents.search(
search_parameters_
)
......