refactor: cleanup audit findings — dedup helpers, prune orphan scores, cache has_frigate_scores

- upload_tracker: extract _pick_mapped_file() private helper; get_lowest_quality_mapped_file
  and get_most_redundant_mapped_file are now one-liners over the same body
- upload_tracker: remove_frigate_file now also prunes the corresponding frigate_scores entry,
  preventing unbounded accumulation of orphaned score entries across replacement cycles
- frigate_api: get_frigate_face_counts delegates to get_all_frigate_person_files, eliminating
  the duplicated "name != 'train' and isinstance(files, list)" filter body
- executor: cache has_frigate_scores(name) as person_has_fscores before the per-file loop;
  refresh it after each remove_frigate_file call and after each scored upload, eliminating
  two redundant disk reads per at-cap file iteration
- executor: casefold() both sides of the recognize_face person-name comparison so a Frigate
  casing normalization or manual-registration casing mismatch does not silently suppress scoring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 18:55:18 +00:00
co-authored by Claude Sonnet 4.6
parent bbbac18207
commit e67f2d9638
3 changed files with 43 additions and 52 deletions
+14 -18
View File
@@ -22,24 +22,6 @@ def _get_faces_data() -> dict | None:
return None
def get_frigate_face_counts() -> dict[str, int] | None:
"""Return {person_name: training_image_count} from Frigate's train directory.
Returns None if FRIGATE_URL is not set or the API is unreachable, so callers
can distinguish "API unavailable" from "person has 0 images."
"""
data = _get_faces_data()
if data is None:
return None
# Response: {person_name: [file, ...], "train": [...], ...}
# "train" is a flat pending list, not a person — skip it.
return {
name: len(files)
for name, files in data.items()
if name != "train" and isinstance(files, list)
}
def get_all_frigate_person_files() -> dict[str, list[str]] | None:
"""Return {person_name: [filename, ...]} for every person in Frigate.
@@ -49,6 +31,8 @@ def get_all_frigate_person_files() -> dict[str, list[str]] | None:
data = _get_faces_data()
if data is None:
return None
# Response: {person_name: [file, ...], "train": [...], ...}
# "train" is a flat pending list, not a person — skip it.
return {
name: files
for name, files in data.items()
@@ -56,6 +40,18 @@ def get_all_frigate_person_files() -> dict[str, list[str]] | None:
}
def get_frigate_face_counts() -> dict[str, int] | None:
"""Return {person_name: training_image_count} from Frigate's train directory.
Returns None if FRIGATE_URL is not set or the API is unreachable, so callers
can distinguish "API unavailable" from "person has 0 images."
"""
all_files = get_all_frigate_person_files()
if all_files is None:
return None
return {name: len(files) for name, files in all_files.items()}
def get_frigate_person_files(person_name: str) -> list[str] | None:
"""Return the list of training filenames for a person in Frigate.