|
20 | 20 | import tempfile |
21 | 21 | import time |
22 | 22 | import zipfile |
| 23 | +from datetime import datetime |
23 | 24 | from typing import Any, Dict, List, Optional, Tuple |
24 | 25 |
|
25 | 26 | from filelock import FileLock |
@@ -225,6 +226,11 @@ def _to_id(self, file: str) -> Tuple[str, str]: |
225 | 226 | id = file.replace(ext, "") |
226 | 227 | return id, ext |
227 | 228 |
|
| 229 | + def _to_label_id(self, file: str) -> Tuple[str, str]: |
| 230 | + if file.lower().endswith(".seg.nrrd"): |
| 231 | + return file[: -len(".seg.nrrd")], ".seg.nrrd" |
| 232 | + return self._to_id(file) |
| 233 | + |
228 | 234 | def _filename(self, id: str, ext: str) -> str: |
229 | 235 | return id + ext |
230 | 236 |
|
@@ -488,7 +494,7 @@ def save_label(self, image_id: str, label_filename: str, label_tag: str, label_i |
488 | 494 | if not obj: |
489 | 495 | raise ImageNotFoundException(f"Image {image_id} not found") |
490 | 496 |
|
491 | | - _, label_ext = self._to_id(os.path.basename(label_filename)) |
| 497 | + _, label_ext = self._to_label_id(os.path.basename(label_filename)) |
492 | 498 | label_id = image_id |
493 | 499 |
|
494 | 500 | logger.info(f"Adding Label: {image_id} => {label_tag} => {label_filename}") |
@@ -540,11 +546,38 @@ def update_label_info(self, label_id: str, label_tag: str, info: Dict[str, Any]) |
540 | 546 | :param label_id: the id of the label we want to add/update info |
541 | 547 | :param label_tag: the matching label tag |
542 | 548 | :param info: a dictionary of custom label information Dict[str, Any] |
| 549 | +
|
| 550 | + The `last_reviewed` field is preserved if the caller already provides the key, |
| 551 | + otherwise we keep the existing value when present. For older reviewer |
| 552 | + metadata that predates `last_reviewed`, we fall back to the label's |
| 553 | + existing `ts` only when the label already carries review metadata; |
| 554 | + otherwise we stamp the current server-side update time. |
543 | 555 | """ |
544 | 556 | label = self._datastore.label(label_id, label_tag) |
545 | 557 | if not label: |
546 | 558 | raise LabelNotFoundException(f"Label: {label_id} Tag: {label_tag} not found") |
547 | 559 |
|
| 560 | + info = dict(info) if info else {} |
| 561 | + if "last_reviewed" not in info: |
| 562 | + has_incoming_review_metadata = any( |
| 563 | + info.get(field) for field in ("status", "level", "comment", "reviewer", "reviewer_name") |
| 564 | + ) |
| 565 | + if has_incoming_review_metadata: |
| 566 | + info["last_reviewed"] = datetime.now().isoformat() |
| 567 | + else: |
| 568 | + existing_last_reviewed = label.info.get("last_reviewed") |
| 569 | + if existing_last_reviewed: |
| 570 | + info["last_reviewed"] = existing_last_reviewed |
| 571 | + else: |
| 572 | + has_existing_review_metadata = any( |
| 573 | + label.info.get(field) for field in ("status", "level", "comment", "reviewer", "reviewer_name") |
| 574 | + ) |
| 575 | + existing_ts = label.info.get("ts") |
| 576 | + if has_existing_review_metadata and isinstance(existing_ts, (int, float)): |
| 577 | + info["last_reviewed"] = datetime.fromtimestamp(existing_ts).isoformat() |
| 578 | + else: |
| 579 | + info["last_reviewed"] = datetime.now().isoformat() |
| 580 | + |
548 | 581 | label.info.update(info) |
549 | 582 | self._update_datastore_file() |
550 | 583 |
|
@@ -613,7 +646,7 @@ def _add_non_existing_labels(self, tag) -> int: |
613 | 646 |
|
614 | 647 | image_ids = list(self._datastore.objects.keys()) |
615 | 648 | for label_file in local_labels: |
616 | | - label_id, label_ext = self._to_id(label_file) |
| 649 | + label_id, label_ext = self._to_label_id(label_file) |
617 | 650 |
|
618 | 651 | obj = self._datastore.objects.get(label_id) |
619 | 652 | if not obj or label_id not in image_ids: |
|
0 commit comments