fix: resolve all linting errors, add tests, bump base image
Linting (ruff): - cli.py: sort relative imports (I001) - jobs.py: remove unused get_people import (F401), wrap long line (E501) - executor.py: remove unused success variable (F841), wrap 4 long lines (E501) Tests (24 passing): - tests/test_config.py: config singleton defaults + env var overrides - tests/test_upload_tracker.py: mark/filter/reset/summary logic - tests/test_immich_api.py: filter_recent_assets date boundary cases - tests/test_jobs.py: _resolve_strategy with LIMIT env var and fallbacks - pyproject.toml: add [tool.pytest.ini_options] testpaths=["tests"] so pytest doesn't scan .venv in CI Security: - Dockerfile: bump CUDA base from 12.6.3 to 12.9.2 to pick up patched Ubuntu packages (fixes Dependabot low-severity alert) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -7,11 +7,11 @@ from rich import print as rprint
|
||||
from rich.prompt import Confirm
|
||||
|
||||
from .config import Config, ConfigManager
|
||||
from .executor import execute_jobs, upload_to_frigate
|
||||
from .immich_api import get_people
|
||||
from .jobs import _show_preview, auto_configure, interactive_configure
|
||||
from .logging import console, setup_logging
|
||||
from .upload_tracker import get_person_summary, reset_person
|
||||
from .executor import execute_jobs, upload_to_frigate
|
||||
from .jobs import auto_configure, interactive_configure, _show_preview
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+17
-7
@@ -242,8 +242,6 @@ def upload_to_frigate(jobs: list[dict]) -> None:
|
||||
|
||||
for fname in person_files:
|
||||
fpath = os.path.join(person_dir, fname)
|
||||
success = False
|
||||
|
||||
for attempt in range(1, max_retries + 1):
|
||||
try:
|
||||
with open(fpath, "rb") as f:
|
||||
@@ -255,7 +253,6 @@ def upload_to_frigate(jobs: list[dict]) -> None:
|
||||
if resp.status_code == 200:
|
||||
uploaded += 1
|
||||
person_uploaded += 1
|
||||
success = True
|
||||
|
||||
# Mark this asset as uploaded so it's skipped on future runs
|
||||
asset_id = asset_map.get(fname)
|
||||
@@ -265,7 +262,10 @@ def upload_to_frigate(jobs: list[dict]) -> None:
|
||||
break
|
||||
else:
|
||||
if attempt < max_retries:
|
||||
logger.warning(f"Upload attempt {attempt}/{max_retries} for {fname}: HTTP {resp.status_code}, retrying...")
|
||||
logger.warning(
|
||||
f"Upload attempt {attempt}/{max_retries} for {fname}:"
|
||||
f" HTTP {resp.status_code}, retrying..."
|
||||
)
|
||||
continue
|
||||
failed += 1
|
||||
person_failed += 1
|
||||
@@ -284,17 +284,27 @@ def upload_to_frigate(jobs: list[dict]) -> None:
|
||||
mark_rejected(asset_id, person_name=name)
|
||||
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as exc:
|
||||
if attempt < max_retries:
|
||||
logger.warning(f"Upload attempt {attempt}/{max_retries} for {fname}: {type(exc).__name__}, retrying...")
|
||||
logger.warning(
|
||||
f"Upload attempt {attempt}/{max_retries} for {fname}:"
|
||||
f" {type(exc).__name__}, retrying..."
|
||||
)
|
||||
continue
|
||||
failed += 1
|
||||
person_failed += 1
|
||||
label = "Connection refused" if isinstance(exc, requests.exceptions.ConnectionError) else "Request timed out (30s)"
|
||||
label = (
|
||||
"Connection refused"
|
||||
if isinstance(exc, requests.exceptions.ConnectionError)
|
||||
else "Request timed out (30s)"
|
||||
)
|
||||
progress.console.print(
|
||||
f" [red]✗ {fname}: {label} (after {max_retries} attempts)[/red]"
|
||||
)
|
||||
except Exception as e:
|
||||
if attempt < max_retries:
|
||||
logger.warning(f"Upload attempt {attempt}/{max_retries} for {fname}: {type(e).__name__}, retrying...")
|
||||
logger.warning(
|
||||
f"Upload attempt {attempt}/{max_retries} for {fname}:"
|
||||
f" {type(e).__name__}, retrying..."
|
||||
)
|
||||
continue
|
||||
failed += 1
|
||||
person_failed += 1
|
||||
|
||||
+5
-2
@@ -11,7 +11,7 @@ from rich.table import Table
|
||||
from .config import Config
|
||||
from .diversity import select_diverse_assets
|
||||
from .embeddings import is_embedding_available, load_embedding_model
|
||||
from .immich_api import fetch_all_assets, filter_recent_assets, get_people
|
||||
from .immich_api import fetch_all_assets, filter_recent_assets
|
||||
from .logging import console
|
||||
from .upload_tracker import filter_already_uploaded
|
||||
|
||||
@@ -231,7 +231,10 @@ def auto_configure(people: list[dict]) -> list[dict]:
|
||||
if min_face_count > 0:
|
||||
valid_people = [p for p in valid_people if p.get("assetCount", 0) >= min_face_count]
|
||||
if valid_people:
|
||||
rprint(f" Filtered to {len(valid_people)} people with ≥{min_face_count} assets (MIN_FACE_COUNT={min_face_count})")
|
||||
rprint(
|
||||
f" Filtered to {len(valid_people)} people with"
|
||||
f" ≥{min_face_count} assets (MIN_FACE_COUNT={min_face_count})"
|
||||
)
|
||||
|
||||
jobs = []
|
||||
for person in valid_people:
|
||||
|
||||
Reference in New Issue
Block a user