diff --git a/api/src/api/v1/summary.py b/api/src/api/v1/summary.py index 84b2f8d..679fd1d 100644 --- a/api/src/api/v1/summary.py +++ b/api/src/api/v1/summary.py @@ -8,7 +8,13 @@ from sqlalchemy.ext.asyncio import AsyncSession from src.api.v1.deps import get_current_user from src.db.models.app_user import AppUser from src.db.session import get_db -from src.domain.schemas import BaseListResponse, SheetResponse +from src.domain.schemas import ( + BaseListResponse, + BaseSingleResponse, + SheetResponse, + Svod2Facets, + Svod2PageResponse, +) from src.services.summary_service import SummaryService @@ -90,7 +96,33 @@ async def get_summary( ) -@router.get("/2/{sheet}/{year}") +def is_svod2_page_mode( + view: Optional[str], + search: Optional[str], + smeta_type: Optional[str], + smeta_direction: Optional[str], + org_name: Optional[str], +) -> bool: + return any( + value is not None + for value in (view, search, smeta_type, smeta_direction, org_name) + ) + + +def _check_summary2_sheet(sheet: str) -> str: + sheet = sheet.upper() + if sheet not in VALID_SUMMARY2_SHEETS: + raise HTTPException( + 404, + f"Лист свода {sheet} должен быть одним из: {', '.join(sorted(VALID_SUMMARY2_SHEETS))}", + ) + return sheet + + +@router.get( + "/2/{sheet}/{year}", + response_model=Svod2PageResponse | BaseListResponse[SheetResponse], +) async def get_summary2( sheet: str, year: int, @@ -100,28 +132,47 @@ async def get_summary2( limit: Optional[int] = Query(default=None, ge=1), sort_by: Literal["org_name", "section_code"] = "org_name", sort_direction: Literal["asc", "desc"] = "asc", + view: Optional[Literal["full", "compact"]] = None, + search: Optional[str] = None, + smeta_type: Optional[str] = None, + smeta_direction: Optional[str] = None, + org_name: Optional[str] = None, db: AsyncSession = Depends(get_db), current_user: AppUser = Depends(get_current_user), -) -> BaseListResponse[SheetResponse]: - sheet = sheet.upper() - if sheet not in VALID_SUMMARY2_SHEETS: - raise HTTPException( - 404, - f"Лист свода {sheet} должен быть одним из: {', '.join(sorted(VALID_SUMMARY2_SHEETS))}", - ) +) -> Svod2PageResponse | BaseListResponse[SheetResponse]: + sheet = _check_summary2_sheet(sheet) + page_mode = is_svod2_page_mode(view, search, smeta_type, smeta_direction, org_name) t0 = time.perf_counter() summary_service = SummaryService(db) - rows = await summary_service.get_svod2_rows( - year=year, - sheet=sheet, - user=current_user, - org_ids=_parse_org_ids(org_ids), - offset=offset, - limit=limit, - sort_by=sort_by, - sort_direction=sort_direction, - ) + total = None + if page_mode: + rows, total = await summary_service.get_svod2_page( + year=year, + sheet=sheet, + user=current_user, + org_ids=_parse_org_ids(org_ids), + offset=offset, + limit=limit, + sort_by=sort_by, + sort_direction=sort_direction, + search=search, + smeta_type=smeta_type, + smeta_direction=smeta_direction, + org_name=org_name, + view=view or "full", + ) + else: + rows = await summary_service.get_svod2_rows( + year=year, + sheet=sheet, + user=current_user, + org_ids=_parse_org_ids(org_ids), + offset=offset, + limit=limit, + sort_by=sort_by, + sort_direction=sort_direction, + ) db_ms = (time.perf_counter() - t0) * 1000 response.headers["X-DB-Time-Ms"] = f"{db_ms:.2f}" @@ -136,11 +187,69 @@ async def get_summary2( "limit": limit, "sort_by": sort_by, "sort_direction": sort_direction, + "view": view, + "search": search, + "smeta_type": smeta_type, + "smeta_direction": smeta_direction, + "org_name": org_name, "user": current_user.id, } logger.info(f"db_time: {extra}", extra=extra) + if page_mode: + return Svod2PageResponse( + count=len(rows), + result=_rows_to_sheet_response(rows), + total=total, + ) return BaseListResponse( count=len(rows), result=_rows_to_sheet_response(rows), ) + + +@router.get("/2/{sheet}/{year}/facets") +async def get_summary2_facets( + sheet: str, + year: int, + response: Response, + org_ids: Optional[str] = None, + search: Optional[str] = None, + smeta_type: Optional[str] = None, + smeta_direction: Optional[str] = None, + org_name: Optional[str] = None, + db: AsyncSession = Depends(get_db), + current_user: AppUser = Depends(get_current_user), +) -> BaseSingleResponse[Svod2Facets]: + sheet = _check_summary2_sheet(sheet) + + t0 = time.perf_counter() + facets = await SummaryService(db).get_svod2_facets( + year=year, + sheet=sheet, + user=current_user, + org_ids=_parse_org_ids(org_ids), + search=search, + smeta_type=smeta_type, + smeta_direction=smeta_direction, + org_name=org_name, + ) + db_ms = (time.perf_counter() - t0) * 1000 + response.headers["X-DB-Time-Ms"] = f"{db_ms:.2f}" + + extra = { + "type": "db_time", + "time_ms": f"{db_ms:.2f}", + "handler": "get_summary2_facets", + "sheet": sheet, + "year": year, + "org_ids": org_ids, + "search": search, + "smeta_type": smeta_type, + "smeta_direction": smeta_direction, + "org_name": org_name, + "user": current_user.id, + } + logger.info(f"db_time: {extra}", extra=extra) + + return BaseSingleResponse(result=facets) diff --git a/api/tests/unit/test_summary2_mode.py b/api/tests/unit/test_summary2_mode.py new file mode 100644 index 0000000..2d95d29 --- /dev/null +++ b/api/tests/unit/test_summary2_mode.py @@ -0,0 +1,10 @@ +import pytest + +from src.api.v1.summary import is_svod2_page_mode + + +@pytest.mark.parametrize("args,expected", [ + ((None, None, None, None, None), False), (("full", None, None, None, None), True), + ((None, "", None, None, None), True), ((None, None, None, None, "x"), True)]) +def test_is_svod2_page_mode(args, expected): + assert is_svod2_page_mode(*args) is expected