TheAlly commited on
Commit
be6595e
·
1 Parent(s): bdcc9e3

Default top_n to None, strip metadata row, post-filter by min_conf

Browse files
Files changed (1) hide show
  1. main.py +22 -1
main.py CHANGED
@@ -22,6 +22,13 @@ def health():
22
  }
23
 
24
 
 
 
 
 
 
 
 
25
  @app.post("/analyze")
26
  async def analyze_audio(
27
  audio: UploadFile = File(...),
@@ -29,7 +36,10 @@ async def analyze_audio(
29
  lat: Optional[float] = Form(None),
30
  lon: Optional[float] = Form(None),
31
  week: Optional[int] = Form(None),
32
- top_n: int = Form(5),
 
 
 
33
  ):
34
  suffix = Path(audio.filename or "audio.wav").suffix or ".wav"
35
  with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp_in:
@@ -54,6 +64,17 @@ async def analyze_audio(
54
  for csv_file in Path(out_dir).glob("*.csv"):
55
  with open(csv_file, newline="") as f:
56
  for row in csv_module.DictReader(f):
 
 
 
 
 
 
 
 
 
 
 
57
  results.append(dict(row))
58
 
59
  return JSONResponse({"results": results})
 
22
  }
23
 
24
 
25
+ # Keys we expect on a real detection row — used to filter out the
26
+ # metadata block BirdNET appends to its CSV output ("File splitting
27
+ # duration", "Segment length", etc., which DictReader otherwise picks
28
+ # up as a phantom row).
29
+ DETECTION_KEYS = {"Start (s)", "End (s)", "Scientific name", "Common name", "Confidence"}
30
+
31
+
32
  @app.post("/analyze")
33
  async def analyze_audio(
34
  audio: UploadFile = File(...),
 
36
  lat: Optional[float] = Form(None),
37
  lon: Optional[float] = Form(None),
38
  week: Optional[int] = Form(None),
39
+ # None lets min_conf gate the results. Pass an int to force "top N per
40
+ # segment regardless of confidence" — useful for debugging silent files,
41
+ # noisy for normal use.
42
+ top_n: Optional[int] = Form(None),
43
  ):
44
  suffix = Path(audio.filename or "audio.wav").suffix or ".wav"
45
  with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp_in:
 
64
  for csv_file in Path(out_dir).glob("*.csv"):
65
  with open(csv_file, newline="") as f:
66
  for row in csv_module.DictReader(f):
67
+ # Skip the trailing metadata block — its keys don't
68
+ # overlap with detection keys.
69
+ if not DETECTION_KEYS.issubset(row.keys()):
70
+ continue
71
+ # Defence-in-depth: drop rows below threshold even
72
+ # when top_n is set, so the response is consistent.
73
+ try:
74
+ if float(row.get("Confidence", 0)) < min_conf:
75
+ continue
76
+ except (TypeError, ValueError):
77
+ continue
78
  results.append(dict(row))
79
 
80
  return JSONResponse({"results": results})