feat: filter tackle analytics by role

This commit is contained in:
ik
2026-09-21 19:48:49 +07:00
parent b7d69619fa
commit 6fa669b4ef
3 changed files with 15 additions and 7 deletions
+10 -5
View File
@@ -4,7 +4,7 @@ from collections import defaultdict
from datetime import datetime, timedelta, timezone
from math import exp, log
from fastapi import APIRouter, Query
from fastapi import APIRouter, HTTPException, Query
from sqlalchemy import select
from sqlalchemy.orm import Session, joinedload, selectinload
@@ -27,10 +27,13 @@ def tackle_combinations(
waterbody: str | None = None,
fish: str | None = None,
method: str | None = None,
role: str | None = None,
hours: int = Query(72, ge=24, le=168),
min_samples: int = Query(3, ge=1, le=100),
min_players: int = Query(2, ge=1, le=100),
) -> list[TackleCombinationOut]:
if role is not None and role not in {"lure", "bait", "rig", "rod", "reel", "line", "hook", "float", "sinker"}:
raise HTTPException(status_code=422, detail="invalid tackle role")
now = datetime.now(timezone.utc)
query = select(CatchReport).options(
joinedload(CatchReport.fish), joinedload(CatchReport.waterbody),
@@ -50,23 +53,25 @@ def tackle_combinations(
groups: dict[tuple[str, str], list[CatchReport]] = defaultdict(list)
for report in db.scalars(query):
for component in report.tackle_components:
if role and component.role != role:
continue
if component.raw_value.strip():
groups[(component.role, component.raw_value.strip())].append(report)
result = []
for (role, value), reports in groups.items():
for (component_role, value), reports in groups.items():
unique_reports = {report.id: report for report in reports}
item_ids = {
component.tackle_item_id
for report in unique_reports.values()
for component in report.tackle_components
if component.role == role and component.raw_value.strip() == value and component.tackle_item_id is not None
if component.role == component_role and component.raw_value.strip() == value and component.tackle_item_id is not None
}
rig_ids = {
component.rig_id
for report in unique_reports.values()
for component in report.tackle_components
if component.role == role and component.raw_value.strip() == value and component.rig_id is not None
if component.role == component_role and component.raw_value.strip() == value and component.rig_id is not None
}
canonical_item_id = next(iter(item_ids)) if len(item_ids) == 1 and not rig_ids else None
canonical_rig_id = next(iter(rig_ids)) if len(rig_ids) == 1 and not item_ids else None
@@ -80,7 +85,7 @@ def tackle_combinations(
) / catches * 100)
enough = catches >= min_samples and unique_players >= min_players
result.append(TackleCombinationOut(
role=role, value=value, tackle_item_id=canonical_item_id, rig_id=canonical_rig_id,
role=component_role, value=value, tackle_item_id=canonical_item_id, rig_id=canonical_rig_id,
catches=catches, unique_players=unique_players,
last_seen_at=last_seen, freshness_score=freshness_score,
status="recommendation" if enough else "insufficient_data",