From 4a8e091a2dcad54777d2cd16d3055628dafd0c22 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Thu, 18 Sep 2025 21:05:47 +0200 Subject: [PATCH 1/3] added new annotation types --- data_store/dto/__init__.py | 6 ++ data_store/dto/annotation.py | 21 +++++- data_store/dto/colour_enum.py | 11 +++ data_store/dto/depth_enum.py | 11 +++ data_store/dto/lighting_enum.py | 11 +++ .../repositories/annotation_repository.py | 21 +++++- tests/integration/conftest.py | 75 +++++++++++-------- tests/unit/test_annotation_repository.py | 27 +++++-- 8 files changed, 137 insertions(+), 46 deletions(-) create mode 100644 data_store/dto/colour_enum.py create mode 100644 data_store/dto/depth_enum.py create mode 100644 data_store/dto/lighting_enum.py diff --git a/data_store/dto/__init__.py b/data_store/dto/__init__.py index 6fbe0de..909ef5a 100644 --- a/data_store/dto/__init__.py +++ b/data_store/dto/__init__.py @@ -5,9 +5,12 @@ Exposes core data transfer objects and enums for use throughout the project. from .angle_enum import AngleEnum from .annotation import Annotation +from .colour_enum import ColourEnum from .contact_enum import ContactEnum +from .depth_enum import DepthEnum from .distance_enum import DistanceEnum from .job import Job +from .lighting_enum import LightingEnum from .point_of_view_enum import PointOfViewEnum __all__ = [ @@ -17,4 +20,7 @@ __all__ = [ "DistanceEnum", "Job", "PointOfViewEnum", + "ColourEnum", + "DepthEnum", + "LightingEnum", ] diff --git a/data_store/dto/annotation.py b/data_store/dto/annotation.py index 97a4427..afeafcd 100644 --- a/data_store/dto/annotation.py +++ b/data_store/dto/annotation.py @@ -1,16 +1,29 @@ """Definition of Annotation DTO.""" from .type_checking_base_model import TypeCheckingBaseModel -from .distance_enum import DistanceEnum from .angle_enum import AngleEnum -from .point_of_view_enum import PointOfViewEnum +from .colour_enum import ColourEnum from .contact_enum import ContactEnum +from .depth_enum import DepthEnum +from .distance_enum import DistanceEnum +from .lighting_enum import LightingEnum +from .point_of_view_enum import PointOfViewEnum class Annotation(TypeCheckingBaseModel): """Data Transfer Object representing an Annotation.""" - distance: DistanceEnum angle: AngleEnum - point_of_view: PointOfViewEnum + angle_uncertainty: bool + colour: ColourEnum + colour_uncertainty: bool contact: ContactEnum + contact_uncertainty: bool + depth: DepthEnum + depth_uncertainty: bool + distance: DistanceEnum + distance_uncertainty: bool + lighting: LightingEnum + lighting_uncertainty: bool + point_of_view: PointOfViewEnum + point_of_view_uncertainty: bool diff --git a/data_store/dto/colour_enum.py b/data_store/dto/colour_enum.py new file mode 100644 index 0000000..a1c114c --- /dev/null +++ b/data_store/dto/colour_enum.py @@ -0,0 +1,11 @@ +"""Definition of ColourEnum DTO.""" + +from enum import StrEnum + + +class ColourEnum(StrEnum): + """Enumeration for Colour values.""" + + HIGH = "high" + MEDIUM = "medium" + LOW = "low" diff --git a/data_store/dto/depth_enum.py b/data_store/dto/depth_enum.py new file mode 100644 index 0000000..23dd52c --- /dev/null +++ b/data_store/dto/depth_enum.py @@ -0,0 +1,11 @@ +"""Definition of DepthEnum DTO.""" + +from enum import StrEnum + + +class DepthEnum(StrEnum): + """Enumeration for Depth values.""" + + HIGH = "high" + MEDIUM = "medium" + LOW = "low" diff --git a/data_store/dto/lighting_enum.py b/data_store/dto/lighting_enum.py new file mode 100644 index 0000000..0e5b52b --- /dev/null +++ b/data_store/dto/lighting_enum.py @@ -0,0 +1,11 @@ +"""Definition of LightingEnum DTO.""" + +from enum import StrEnum + + +class LightingEnum(StrEnum): + """Enumeration for Lighting values.""" + + HIGH = "high" + MEDIUM = "medium" + LOW = "low" diff --git a/data_store/repositories/annotation_repository.py b/data_store/repositories/annotation_repository.py index b40baa1..bdbaa2e 100644 --- a/data_store/repositories/annotation_repository.py +++ b/data_store/repositories/annotation_repository.py @@ -11,10 +11,13 @@ from python_repositories.adapters import MinioAdapter from data_store.interfaces import AnnotationRepositoryInterface from data_store.dto import ( Annotation, - DistanceEnum, AngleEnum, - PointOfViewEnum, + ColourEnum, ContactEnum, + DepthEnum, + DistanceEnum, + LightingEnum, + PointOfViewEnum, ) @@ -55,10 +58,20 @@ class AnnotationRepository(MinioAdapter, AnnotationRepositoryInterface): data_str = buffer.read().decode(self.encoding) data = json.loads(data_str) annotation = Annotation( - distance=DistanceEnum(data["distance"]), angle=AngleEnum(data["angle"]), - point_of_view=PointOfViewEnum(data["point_of_view"]), + angle_uncertainty=bool(data["angle_uncertainty"]), + colour=ColourEnum(data["colour"]), + colour_uncertainty=bool(data["colour_uncertainty"]), contact=ContactEnum(data["contact"]), + contact_uncertainty=bool(data["contact_uncertainty"]), + depth=DepthEnum(data["depth"]), + depth_uncertainty=bool(data["depth_uncertainty"]), + distance=DistanceEnum(data["distance"]), + distance_uncertainty=bool(data["distance_uncertainty"]), + lighting=LightingEnum(data["lighting"]), + lighting_uncertainty=bool(data["lighting_uncertainty"]), + point_of_view=PointOfViewEnum(data["point_of_view"]), + point_of_view_uncertainty=bool(data["point_of_view_uncertainty"]), ) except (KeyError, ValueError) as e: self.logger.error( diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 16f2dc8..06a115d 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -23,10 +23,13 @@ from data_store.repositories import ( ) from data_store.dto import ( Annotation, - DistanceEnum, AngleEnum, - PointOfViewEnum, + ColourEnum, ContactEnum, + DepthEnum, + DistanceEnum, + LightingEnum, + PointOfViewEnum, Job, ) @@ -114,35 +117,35 @@ def job_id_list() -> Generator[list[UUID]]: ] -@pytest.fixture(scope="session") -def csv_data_file( - job_id_list: list[UUID], -) -> Generator[Path]: - """Create a CSV data file for testing.""" - # Create csv content - csv_data = pd.DataFrame( - { - "job_id": job_id_list, - "distance": [ - "close", - "long", - "wrong", - ], - "angle": ["high", "low", "wrong"], - "point_of_view": ["frontal", "oblique", "wrong"], - "contact": ["offer", "demand", "wrong"], - } - ) - # Create data directory and file path - data_dir = Path(tempfile.mkdtemp()) - data_path = data_dir / "annotations.csv" - # Write CSV content to file - csv_data.to_csv(data_path, header=True, index=False) +# @pytest.fixture(scope="session") +# def csv_data_file( +# job_id_list: list[UUID], +# ) -> Generator[Path]: +# """Create a CSV data file for testing.""" +# # Create csv content +# csv_data = pd.DataFrame( +# { +# "job_id": job_id_list, +# "distance": [ +# "close", +# "long", +# "wrong", +# ], +# "angle": ["high", "low", "wrong"], +# "point_of_view": ["frontal", "oblique", "wrong"], +# "contact": ["offer", "demand", "wrong"], +# } +# ) +# # Create data directory and file path +# data_dir = Path(tempfile.mkdtemp()) +# data_path = data_dir / "annotations.csv" +# # Write CSV content to file +# csv_data.to_csv(data_path, header=True, index=False) - yield data_path +# yield data_path - # Cleanup - shutil.rmtree(data_dir) +# # Cleanup +# shutil.rmtree(data_dir) @pytest.fixture(scope="session") @@ -245,10 +248,20 @@ def annotation_repository() -> Generator[AnnotationRepository]: def annotation() -> Generator[Annotation]: """Provide a sample annotation for testing.""" annotation = Annotation( - distance=DistanceEnum.CLOSE, angle=AngleEnum.HIGH, - point_of_view=PointOfViewEnum.FRONTAL, + angle_uncertainty=True, + colour=ColourEnum.HIGH, + colour_uncertainty=False, contact=ContactEnum.OFFER, + contact_uncertainty=True, + depth=DepthEnum.LOW, + depth_uncertainty=False, + distance=DistanceEnum.CLOSE, + distance_uncertainty=True, + lighting=LightingEnum.MEDIUM, + lighting_uncertainty=False, + point_of_view=PointOfViewEnum.FRONTAL, + point_of_view_uncertainty=True, ) yield annotation diff --git a/tests/unit/test_annotation_repository.py b/tests/unit/test_annotation_repository.py index e8c6b07..a87309d 100644 --- a/tests/unit/test_annotation_repository.py +++ b/tests/unit/test_annotation_repository.py @@ -7,10 +7,13 @@ from uuid import UUID from data_store.repositories.annotation_repository import AnnotationRepository from data_store.dto import ( Annotation, - DistanceEnum, AngleEnum, - PointOfViewEnum, + ColourEnum, ContactEnum, + DepthEnum, + DistanceEnum, + LightingEnum, + PointOfViewEnum, ) @@ -47,11 +50,21 @@ class TestAnnotationRepository(unittest.TestCase): cls.job_id = UUID("d87022f8-4169-4d69-8512-bbd65cc1cb23") cls.bad_job_id = "not-a-uuid" cls.annotation = Annotation( - distance=DistanceEnum.CLOSE, - angle=AngleEnum.HIGH, - point_of_view=PointOfViewEnum.FRONTAL, - contact=ContactEnum.OFFER, - ) + angle=AngleEnum.HIGH, + angle_uncertainty=True, + colour=ColourEnum.HIGH, + colour_uncertainty=False, + contact=ContactEnum.OFFER, + contact_uncertainty=True, + depth=DepthEnum.LOW, + depth_uncertainty=False, + distance=DistanceEnum.CLOSE, + distance_uncertainty=True, + lighting=LightingEnum.MEDIUM, + lighting_uncertainty=False, + point_of_view=PointOfViewEnum.FRONTAL, + point_of_view_uncertainty=True, + ) cls.bad_annotation = {"not_an_annotation": "irrelevant value"} @classmethod From 63ed329fd95943d4176e58da4107ec530b5505b7 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Thu, 18 Sep 2025 21:06:59 +0200 Subject: [PATCH 2/3] code quality fixes --- tests/integration/conftest.py | 4 ---- tests/unit/test_annotation_repository.py | 30 ++++++++++++------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 06a115d..f4a18ce 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -2,14 +2,10 @@ import logging import os -import shutil -import tempfile -from pathlib import Path import json from collections.abc import Generator from uuid import UUID from io import BytesIO -import pandas as pd from PIL import Image import pytest import structlog diff --git a/tests/unit/test_annotation_repository.py b/tests/unit/test_annotation_repository.py index a87309d..868b27c 100644 --- a/tests/unit/test_annotation_repository.py +++ b/tests/unit/test_annotation_repository.py @@ -50,21 +50,21 @@ class TestAnnotationRepository(unittest.TestCase): cls.job_id = UUID("d87022f8-4169-4d69-8512-bbd65cc1cb23") cls.bad_job_id = "not-a-uuid" cls.annotation = Annotation( - angle=AngleEnum.HIGH, - angle_uncertainty=True, - colour=ColourEnum.HIGH, - colour_uncertainty=False, - contact=ContactEnum.OFFER, - contact_uncertainty=True, - depth=DepthEnum.LOW, - depth_uncertainty=False, - distance=DistanceEnum.CLOSE, - distance_uncertainty=True, - lighting=LightingEnum.MEDIUM, - lighting_uncertainty=False, - point_of_view=PointOfViewEnum.FRONTAL, - point_of_view_uncertainty=True, - ) + angle=AngleEnum.HIGH, + angle_uncertainty=True, + colour=ColourEnum.HIGH, + colour_uncertainty=False, + contact=ContactEnum.OFFER, + contact_uncertainty=True, + depth=DepthEnum.LOW, + depth_uncertainty=False, + distance=DistanceEnum.CLOSE, + distance_uncertainty=True, + lighting=LightingEnum.MEDIUM, + lighting_uncertainty=False, + point_of_view=PointOfViewEnum.FRONTAL, + point_of_view_uncertainty=True, + ) cls.bad_annotation = {"not_an_annotation": "irrelevant value"} @classmethod From 8c061ce8d5320eb3bcf96252d7f781766c194222 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Thu, 18 Sep 2025 21:09:47 +0200 Subject: [PATCH 3/3] sped up cron schedule temporary --- .gitea/workflows/sync-annotations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/sync-annotations.yml b/.gitea/workflows/sync-annotations.yml index 257e3d3..ea2f4ab 100644 --- a/.gitea/workflows/sync-annotations.yml +++ b/.gitea/workflows/sync-annotations.yml @@ -3,7 +3,7 @@ name: Sync Label Studio on: schedule: # This job will run every day at 03:00 AM - - cron: "0 3 * * *" + - cron: "0/10 * * * *" jobs: sync_storage: