Commit 2ad34fc7 authored by Open Science Conservation Fund's avatar Open Science Conservation Fund
Browse files

fixed cs rest classification tests; new tests for msgpack transport

parent 3c6c5570
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -693,6 +693,11 @@ class CSBBoxesSerializer(serializers.Serializer):
        file_fps = self.context.get("file_fps") or 0
        bboxes = instance.bboxes
        first_frame = instance.first_frame_index

        # Handle None case: treat as 0 (default for images and first occurrence)
        if first_frame is None:
            first_frame = 0

        if bboxes and len(bboxes) == 4 and first_frame <= file_fps:
            return {
                "left": bboxes[0],
+20 −10
Original line number Diff line number Diff line
@@ -465,7 +465,8 @@ class CSMediaTests(BaseAPITestCase):
        self.dynamic_ai_classification1 = AIClassificationDynamicAttrsFactory(
            classification=self.ai_classification1,
            observation_type=ObservationType.ANIMAL,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )
        self.classification2 = Classification.objects.get(
            project=self.classification_project, resource=self.resource2
@@ -479,7 +480,8 @@ class CSMediaTests(BaseAPITestCase):
        self.dynamic_ai_classification2 = AIClassificationDynamicAttrsFactory(
            classification=self.ai_classification2,
            observation_type=ObservationType.ANIMAL,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )
        self.classification3 = Classification.objects.get(
            project=self.classification_project, resource=self.resource3
@@ -494,7 +496,8 @@ class CSMediaTests(BaseAPITestCase):
            classification=self.user_classification3,
            observation_type=ObservationType.ANIMAL,
            species=self.species1,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )
        self.classification4 = Classification.objects.get(
            project=self.classification_project, resource=self.resource4
@@ -511,7 +514,8 @@ class CSMediaTests(BaseAPITestCase):
            classification=self.user_classification4,
            observation_type=ObservationType.ANIMAL,
            species=self.species2,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )

        self.classification5 = ClassificationFactory(
@@ -537,7 +541,8 @@ class CSMediaTests(BaseAPITestCase):
                classification=self.user_classification5,
                observation_type=ObservationType.ANIMAL,
                species=self.species1,
                bboxes=[[0.444, 0.444, 0.333, 0.333]],
                bboxes=[0.444, 0.444, 0.333, 0.333],
                first_frame_index=0,
            )
        )
        # Set source_classification to AI classification (so it's not expert-approved)
@@ -817,7 +822,8 @@ class CSMediaScrollTests(BaseAPITestCase):
        self.dynamic_ai_classification1 = AIClassificationDynamicAttrsFactory(
            classification=self.ai_classification1,
            observation_type=ObservationType.ANIMAL,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )
        self.classification2 = Classification.objects.get(
            project=self.classification_project, resource=self.resource2
@@ -831,7 +837,8 @@ class CSMediaScrollTests(BaseAPITestCase):
        self.dynamic_ai_classification2 = AIClassificationDynamicAttrsFactory(
            classification=self.ai_classification2,
            observation_type=ObservationType.ANIMAL,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )
        self.classification3 = Classification.objects.get(
            project=self.classification_project, resource=self.resource3
@@ -846,7 +853,8 @@ class CSMediaScrollTests(BaseAPITestCase):
            classification=self.user_classification3,
            observation_type=ObservationType.ANIMAL,
            species=self.species1,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )
        self.classification4 = Classification.objects.get(
            project=self.classification_project, resource=self.resource4
@@ -861,7 +869,8 @@ class CSMediaScrollTests(BaseAPITestCase):
            classification=self.user_classification4,
            observation_type=ObservationType.ANIMAL,
            species=self.species2,
            bboxes=[[0.444, 0.444, 0.333, 0.333]],
            bboxes=[0.444, 0.444, 0.333, 0.333],
            first_frame_index=0,
        )

        self.classification5 = ClassificationFactory(
@@ -886,7 +895,8 @@ class CSMediaScrollTests(BaseAPITestCase):
                classification=self.user_classification5,
                observation_type=ObservationType.ANIMAL,
                species=self.species1,
                bboxes=[[0.444, 0.444, 0.333, 0.333]],
                bboxes=[0.444, 0.444, 0.333, 0.333],
                first_frame_index=0,
            )
        )
        self.classification1.source_classification = self.ai_classification1
+9 −0
Original line number Diff line number Diff line
@@ -268,6 +268,15 @@ class SmartFrameService:
            if dyn_id is None:
                continue

            # Skip objects with non-numeric IDs (should not happen if serializer filtered properly)
            # This is a defensive check for msgpack data from other sources
            if not isinstance(dyn_id, int):
                try:
                    dyn_id = int(dyn_id)
                except (ValueError, TypeError):
                    # Skip objects that couldn't be matched to dynamic attrs
                    continue

            # Delete existing rows for this dynamic attrs id
            self.filter_ts(
                classification=cls,
+11 −0
Original line number Diff line number Diff line
@@ -478,6 +478,17 @@ class SingleClassifySerializer(BaseClassifyMixin, serializers.ModelSerializer):

        # Now save frames from msgpack if exist and rebuild msgpack file for user classification
        if bboxes_msgpack:
            # Filter out unmatched objects (those with string IDs that were never matched to object_id)
            # Only keep objects whose "id" was updated to a numeric database ID
            matched_objects = [
                obj
                for obj in bboxes_msgpack["objects"]
                if isinstance(obj.get("id"), int)
            ]
            bboxes_msgpack["objects"] = matched_objects

            # Only process if there are matched objects
            if matched_objects:
                frames_service = SmartFrameService()
                frames_service.upsert_msgpack(
                    payload=bboxes_msgpack,
+2 −0
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ class ClassificationFactory(factory.django.DjangoModelFactory):
    created_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(minutes=10))
    updated_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(minutes=10))
    updated_by = owner
    # Helper field that caches resource's date_recorded for efficient ordering
    date_recorded = factory.LazyAttribute(lambda o: o.resource.date_recorded)

    classification_type = ClassificationType.FINAL
    is_approved = False
Loading