Support browser calibration summaries in evidence export
This commit is contained in:
@@ -442,6 +442,12 @@ CALIBRATION_SUMMARY_PATH=/mnt/user/appdata/geointel/artifacts/detection-calibrat
|
||||
bash scripts/export_detection_calibration_evidence.sh http://192.168.10.150:1202
|
||||
```
|
||||
|
||||
Browser Detection Lab calibration summary exports are supported too:
|
||||
|
||||
```bash
|
||||
bash scripts/export_detection_calibration_evidence.sh http://192.168.10.150:1202 ./detection-calibration-summary.json
|
||||
```
|
||||
|
||||
The evidence export reads each persisted `quality_check_id`, calls the existing
|
||||
QA evidence GeoJSON endpoint, writes `calibration_evidence.geojson`,
|
||||
`calibration_evidence_summary.json` and a standalone
|
||||
|
||||
@@ -10,8 +10,12 @@ Usage:
|
||||
or:
|
||||
bash scripts/export_detection_calibration_evidence.sh [base_url] /path/to/calibration_summary.json
|
||||
|
||||
or with the browser Detection Lab export:
|
||||
bash scripts/export_detection_calibration_evidence.sh [base_url] /path/to/detection-calibration-summary.json
|
||||
|
||||
Required input:
|
||||
CALIBRATION_SUMMARY_PATH calibration_summary.json produced by run_detection_calibration_sweep.sh.
|
||||
CALIBRATION_SUMMARY_PATH calibration_summary.json produced by run_detection_calibration_sweep.sh
|
||||
or detection-calibration-summary.json from the Detection Lab.
|
||||
|
||||
Optional environment:
|
||||
CALIBRATION_EVIDENCE_MODE all or best, default: all.
|
||||
@@ -86,9 +90,42 @@ summary_path, manifest_path, mode = sys.argv[1:4]
|
||||
with open(summary_path, "r", encoding="utf-8") as handle:
|
||||
summary = json.load(handle)
|
||||
|
||||
items = summary.get("items") or []
|
||||
def normalize_calibration_items(summary):
|
||||
root_project_id = summary.get("project_id")
|
||||
if summary.get("export_type") == "detection_calibration_summary":
|
||||
rows = summary.get("rows") or []
|
||||
quality_check_ids = summary.get("quality_check_ids") or []
|
||||
items = []
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
quality_check_id = row.get("quality_check_id")
|
||||
if not quality_check_id:
|
||||
continue
|
||||
items.append(
|
||||
{
|
||||
"project_id": row.get("project_id") or root_project_id,
|
||||
"analysis_run_id": row.get("analysis_run_id"),
|
||||
"job_id": row.get("job_id"),
|
||||
"quality_check_id": quality_check_id,
|
||||
"threshold": row.get("threshold"),
|
||||
"detection_count": row.get("detection_count"),
|
||||
"quality_score": row.get("quality_score") or row.get("f1_score"),
|
||||
"precision": row.get("precision"),
|
||||
"recall": row.get("recall"),
|
||||
"f1_score": row.get("f1_score"),
|
||||
}
|
||||
)
|
||||
if quality_check_ids and not items:
|
||||
raise SystemExit("detection-calibration-summary.json has quality_check_ids but no export rows")
|
||||
return items
|
||||
return summary.get("items") or []
|
||||
|
||||
items = normalize_calibration_items(summary)
|
||||
if mode == "best":
|
||||
best = summary.get("best_by_score")
|
||||
if best is None and items:
|
||||
best = max(items, key=lambda item: item.get("quality_score") if isinstance(item.get("quality_score"), (int, float)) else float("-inf"))
|
||||
if not isinstance(best, dict):
|
||||
raise SystemExit("calibration_summary.json has no best_by_score object")
|
||||
items = [best]
|
||||
@@ -133,9 +170,46 @@ summary_path, output_dir, mode = sys.argv[1:4]
|
||||
with open(summary_path, "r", encoding="utf-8") as handle:
|
||||
calibration_summary = json.load(handle)
|
||||
|
||||
selected_items = calibration_summary.get("items") or []
|
||||
def normalize_calibration_items(summary):
|
||||
root_project_id = summary.get("project_id")
|
||||
if summary.get("export_type") == "detection_calibration_summary":
|
||||
rows = summary.get("rows") or []
|
||||
quality_check_ids = summary.get("quality_check_ids") or []
|
||||
items = []
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
quality_check_id = row.get("quality_check_id")
|
||||
if not quality_check_id:
|
||||
continue
|
||||
items.append(
|
||||
{
|
||||
"project_id": row.get("project_id") or root_project_id,
|
||||
"analysis_run_id": row.get("analysis_run_id"),
|
||||
"job_id": row.get("job_id"),
|
||||
"quality_check_id": quality_check_id,
|
||||
"threshold": row.get("threshold"),
|
||||
"detection_count": row.get("detection_count"),
|
||||
"quality_score": row.get("quality_score") or row.get("f1_score"),
|
||||
"precision": row.get("precision"),
|
||||
"recall": row.get("recall"),
|
||||
"f1_score": row.get("f1_score"),
|
||||
}
|
||||
)
|
||||
if quality_check_ids and not items:
|
||||
raise SystemExit("detection-calibration-summary.json has quality_check_ids but no export rows")
|
||||
return items
|
||||
return summary.get("items") or []
|
||||
|
||||
selected_items = normalize_calibration_items(calibration_summary)
|
||||
if mode == "best":
|
||||
selected_items = [calibration_summary["best_by_score"]]
|
||||
best = calibration_summary.get("best_by_score")
|
||||
if best is None and selected_items:
|
||||
best = max(
|
||||
selected_items,
|
||||
key=lambda item: item.get("quality_score") if isinstance(item.get("quality_score"), (int, float)) else float("-inf"),
|
||||
)
|
||||
selected_items = [best]
|
||||
items_by_quality_check = {str(item["quality_check_id"]): item for item in selected_items}
|
||||
|
||||
combined_features = []
|
||||
|
||||
Reference in New Issue
Block a user