Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions lightly_studio/src/lightly_studio/core/file_outcome_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

import contextlib
import logging
from collections.abc import Iterator
from collections.abc import Iterator, Mapping
from dataclasses import dataclass, field
from enum import Enum
from os import PathLike
Expand Down Expand Up @@ -94,9 +94,13 @@ class FileOutcomeReport:
Attributes:
max_examples_per_outcome: Maximum number of example paths kept per
outcome.
label_overrides: Per-outcome display-label overrides used only by
`log_summary()`; any outcome absent from the dict falls back to
`outcome.value`. Defaults to empty (add-images behavior).
"""

max_examples_per_outcome: int = DEFAULT_MAX_EXAMPLES_PER_OUTCOME
label_overrides: Mapping[FileOutcome, str] = field(default_factory=dict)
_counts: dict[FileOutcome, int] = field(
default_factory=lambda: dict.fromkeys(FileOutcome, 0),
init=False,
Expand Down Expand Up @@ -173,9 +177,17 @@ def raise_if_all_failed(self) -> None:

def log_summary(self) -> None:
"""Log a single end-of-run summary of counts and example paths."""
counts = ", ".join(f"{outcome.value}={self._counts[outcome]}" for outcome in FileOutcome)
logger.info(f"File outcomes: {counts}.")
counts = ", ".join(
f"{self._label(outcome)}={self._counts[outcome]}" for outcome in FileOutcome
)
logger.info(f"File processing result: {counts}.")
for outcome in FileOutcome:
if outcome == FileOutcome.ADDED:
continue
examples = self._example_paths[outcome]
if examples:
logger.info(f"Example {outcome.value} paths: {', '.join(examples)}.")
logger.info(f"Examples '{self._label(outcome)}': {', '.join(examples)}.")

def _label(self, outcome: FileOutcome) -> str:
"""Return the display label for `outcome`, honoring `label_overrides`."""
return self.label_overrides.get(outcome, outcome.value)
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def load_and_preprocess(filepath: str) -> torch.Tensor | None:
)

kept_indices_set = set(result.kept_indices)
report = FileOutcomeReport()
report = FileOutcomeReport(label_overrides={FileOutcome.ADDED: "embedded"})
for index, filepath in enumerate(filepaths):
outcome = FileOutcome.ADDED if index in kept_indices_set else FileOutcome.BROKEN
report.record(path=filepath, outcome=outcome)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_from_labelformat__duplication(

log_text = caplog.text
assert "added=0, already_present=1" in log_text
assert "Example already_present paths:" in log_text
assert "Examples 'already_present':" in log_text
assert "/fake/path/images/image.jpg" in log_text

def test_from_labelformat__annotations_synced_images(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def test_dataset_add_images_from_path__duplication(

log_text = caplog.text
assert "added=2, already_present=4" in log_text
assert "Example already_present paths:" in log_text
assert "Examples 'already_present':" in log_text
assert f"{images_path}" in log_text

def test_dataset_add_images_from_path__dont_embed(
Expand Down
41 changes: 40 additions & 1 deletion lightly_studio/tests/core/test_file_outcome_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,44 @@ def test_log_summary(self, caplog: pytest.LogCaptureFixture) -> None:
text = caplog.text
assert "added=1" in text
assert "missing=1" in text
assert "a.jpg" in text
# Examples are only shown for non-ADDED outcomes.
assert "b.jpg" in text
assert "Examples 'missing'" in text
assert "a.jpg" not in text
assert "Examples 'added'" not in text

def test_log_summary__label_overrides(self, caplog: pytest.LogCaptureFixture) -> None:
report = FileOutcomeReport(label_overrides={FileOutcome.ADDED: "embedded"})
report.record(path="a.jpg", outcome=FileOutcome.ADDED)
report.record(path="b.jpg", outcome=FileOutcome.MISSING)

with caplog.at_level(logging.INFO):
report.log_summary()

text = caplog.text
# Overridden outcome uses the custom label.
assert "embedded=1" in text
assert "added=1" not in text
# Non-overridden outcomes still render their default labels.
assert "missing=1" in text

def test_log_summary__label_overrides_are_general(
self, caplog: pytest.LogCaptureFixture
) -> None:
report = FileOutcomeReport(
label_overrides={
FileOutcome.ADDED: "embedded",
FileOutcome.MISSING: "not_found",
}
)
report.record(path="a.jpg", outcome=FileOutcome.ADDED)
report.record(path="b.jpg", outcome=FileOutcome.MISSING)

with caplog.at_level(logging.INFO):
report.log_summary()

text = caplog.text
assert "embedded=1" in text
assert "not_found=1" in text
assert "Examples 'not_found'" in text
assert "missing=1" not in text
Loading