perf(api): свод 2 — новый режим страниц с total и эндпоинт вариантов фильтров
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e3a9a08e84
commit
db28138b5b
@ -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)
|
||||
|
||||
10
api/tests/unit/test_summary2_mode.py
Normal file
10
api/tests/unit/test_summary2_mode.py
Normal file
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user