diff --git a/routes/reslevis.py b/routes/reslevis.py index b58c407..f807530 100644 --- a/routes/reslevis.py +++ b/routes/reslevis.py @@ -1,3 +1,4 @@ +import asyncio from fastapi import APIRouter, Depends, HTTPException, Query, Request import httpx import config_env @@ -151,10 +152,75 @@ CORE_GET_SYNC = { "/reslevis/getGateways": (gateway_repo, _normalize_gateway), "/reslevis/getZones": (zone_repo, _normalize_zone), "/reslevis/getTrackers": (tracker_repo, _normalize_tracker), - "/reslevis/getTracks": (track_repo, _normalize_track), } +async def _core_get_json( + client: httpx.AsyncClient, + path: str, + params: Optional[dict] = None, +): + resp = await client.get(f"{CORE_BASE_URL}{path}", params=params) + if resp.status_code >= 400: + detail = resp.text.strip() or "CORE request failed" + raise HTTPException(status_code=resp.status_code, detail=detail) + try: + return resp.json() + except ValueError as exc: + raise HTTPException(status_code=502, detail="Invalid CORE response") from exc + + +async def _fetch_tracks_for_tracker( + client: httpx.AsyncClient, + tracker_id: str, + params: Optional[dict] = None, +) -> List[dict]: + payload = await _core_get_json(client, f"/reslevis/getTracks/{tracker_id}", params=params) + if not isinstance(payload, list): + raise HTTPException(status_code=502, detail="Unexpected CORE response type") + return [_normalize_track(row) for row in payload if isinstance(row, dict)] + + +def _sort_tracks_desc(rows: List[dict]) -> List[dict]: + return sorted(rows, key=lambda row: row.get("timestamp") or "", reverse=True) + + +async def _fetch_all_tracks(params: dict) -> List[dict]: + async with httpx.AsyncClient(timeout=30.0, verify=False) as client: + try: + payload = await _core_get_json(client, "/reslevis/getTracks", params=params) + if not isinstance(payload, list): + raise HTTPException(status_code=502, detail="Unexpected CORE response type") + return [_normalize_track(row) for row in payload if isinstance(row, dict)] + except HTTPException as exc: + if exc.status_code != 404: + raise + + trackers_payload = await _core_get_json(client, "/reslevis/getTrackers") + if not isinstance(trackers_payload, list): + raise HTTPException(status_code=502, detail="Unexpected CORE tracker response type") + + tracker_ids = [] + for item in trackers_payload: + if not isinstance(item, dict): + continue + tracker_id = item.get("id") + if tracker_id: + tracker_ids.append(str(tracker_id)) + + batches = await asyncio.gather( + *[_fetch_tracks_for_tracker(client, tracker_id, params) for tracker_id in tracker_ids] + ) + + merged = [row for batch in batches for row in batch] + merged = _sort_tracks_desc(merged) + + limit = params.get("limit") + if isinstance(limit, int): + return merged[:limit] + return merged + + @router.get( "/getGateways", response_model=List[GatewayItem], @@ -324,12 +390,27 @@ def removeTrackerZone(tracker_zone_id: str): @router.get( "/getTracks", - response_model=List[TrackItem], + response_model=List[TrackHistoryItem], tags=["Reslevis"], dependencies=[Depends(get_current_user)], ) -def getTracks(): - return track_repo.list() +async def getTracks( + limit: Optional[int] = Query(None, ge=1), + from_: Optional[str] = Query(None, alias="from"), + to: Optional[str] = Query(None), +): + params = {} + if limit is not None: + params["limit"] = limit + if from_: + params["from"] = from_ + if to: + params["to"] = to + + try: + return await _fetch_all_tracks(params) + except httpx.RequestError as exc: + raise HTTPException(status_code=502, detail=f"CORE request failed: {exc}") from exc @router.get( @@ -354,27 +435,10 @@ async def getTrack( try: async with httpx.AsyncClient(timeout=30.0, verify=False) as client: - resp = await client.get( - f"{CORE_BASE_URL}/reslevis/getTracks/{tracker_id}", - params=params, - ) + return await _fetch_tracks_for_tracker(client, tracker_id, params) except httpx.RequestError as exc: raise HTTPException(status_code=502, detail=f"CORE request failed: {exc}") from exc - if resp.status_code >= 400: - detail = resp.text.strip() or "CORE request failed" - raise HTTPException(status_code=resp.status_code, detail=detail) - - try: - payload = resp.json() - except ValueError as exc: - raise HTTPException(status_code=502, detail="Invalid CORE response") from exc - - if not isinstance(payload, list): - raise HTTPException(status_code=502, detail="Unexpected CORE response type") - - return [_normalize_track(row) for row in payload if isinstance(row, dict)] - @router.post("/postTrack", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) def postTrack(item: TrackItem):