Skip to content

Commit 67e136b

Browse files
committed
fix: example captures and taxa count speed up
1 parent 2c86d14 commit 67e136b

File tree

3 files changed

+41
-20
lines changed

3 files changed

+41
-20
lines changed

ami/main/api/serializers.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ class Meta:
8484
]
8585

8686

87+
class ExampleSourceImageNestedSerializer(DefaultSerializer):
88+
class Meta:
89+
model = SourceImage
90+
fields = [
91+
"id",
92+
"details",
93+
"url",
94+
"width",
95+
"height",
96+
"timestamp",
97+
]
98+
99+
87100
class DeviceNestedSerializer(DefaultSerializer):
88101
class Meta:
89102
model = Device
@@ -252,8 +265,7 @@ class EventListSerializer(DefaultSerializer):
252265
deployment = DeploymentNestedSerializer(
253266
read_only=True,
254267
)
255-
example_captures = SourceImageNestedSerializer(many=True, read_only=True)
256-
# captures = serializers.StringRelatedField(many=True, read_only=True)
268+
example_captures = ExampleSourceImageNestedSerializer(many=True, read_only=True)
257269
captures = serializers.SerializerMethodField()
258270

259271
class Meta:
@@ -1126,8 +1138,6 @@ class EventSerializer(DefaultSerializer):
11261138
start = serializers.DateTimeField(read_only=True)
11271139
end = serializers.DateTimeField(read_only=True)
11281140
capture_page_offset = serializers.SerializerMethodField()
1129-
occurrences_count = serializers.SerializerMethodField()
1130-
taxa_count = serializers.SerializerMethodField()
11311141

11321142
class Meta:
11331143
model = Event
@@ -1210,18 +1220,6 @@ def get_capture_page_offset(self, obj) -> int | None:
12101220

12111221
return offset
12121222

1213-
def get_occurrences_count(self, obj):
1214-
# This will display a count of the occurrences that are greater than the current classification threshold
1215-
# But this value will be different from the occurrences_count field, which is used for sorting & filtering
1216-
# @TODO refactor how the classification_threshold is used everywhere! delete it!
1217-
return obj.get_occurrences_count(
1218-
classification_threshold=get_active_classification_threshold(self.context["request"])
1219-
)
1220-
1221-
def get_taxa_count(self, obj):
1222-
# @TODO refactor how the classification_threshold is used everywhere! delete it!
1223-
return obj.taxa_count(classification_threshold=get_active_classification_threshold(self.context["request"]))
1224-
12251223

12261224
class EventTimelineSourceImageSerializer(DefaultSerializer):
12271225
class Meta:

ami/main/api/views.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,29 @@ def get_queryset(self) -> QuerySet:
242242
duration=models.F("end") - models.F("start"),
243243
).select_related("deployment", "project")
244244

245+
if self.action == "list":
246+
num_example_captures = 1
247+
qs = qs.prefetch_related(
248+
Prefetch(
249+
"captures",
250+
queryset=SourceImage.objects.order_by("-size").select_related(
251+
"deployment",
252+
"deployment__data_source",
253+
)[:num_example_captures],
254+
to_attr="example_captures",
255+
)
256+
)
257+
258+
qs = qs.annotate(
259+
taxa_count=models.Count(
260+
"occurrences__determination",
261+
distinct=True,
262+
filter=models.Q(
263+
occurrences__determination_score__gte=get_active_classification_threshold(self.request),
264+
),
265+
),
266+
)
267+
245268
return qs
246269

247270
@action(detail=True, methods=["get"], name="timeline")

ami/main/models.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,17 +727,16 @@ def stats(self) -> dict[str, int | None]:
727727
)
728728

729729
def taxa_count(self, classification_threshold: int | None = None) -> int:
730-
return self.taxa(classification_threshold).count()
730+
# Move this to a pre-calculated field or prefetch_related in the view
731+
# return self.taxa(classification_threshold).count()
732+
return 0
731733

732734
def taxa(self, classification_threshold: int | None = None) -> models.QuerySet["Taxon"]:
733735
return Taxon.objects.filter(
734736
Q(occurrences__event=self),
735737
occurrences__determination_score__gte=classification_threshold or settings.DEFAULT_CONFIDENCE_THRESHOLD,
736738
).distinct()
737739

738-
def example_captures(self, num=5):
739-
return SourceImage.objects.filter(event=self).order_by("-size")[:num]
740-
741740
def first_capture(self):
742741
return SourceImage.objects.filter(event=self).order_by("timestamp").first()
743742

@@ -1183,6 +1182,7 @@ def public_url(self) -> str:
11831182
11841183
@TODO add support for thumbnail URLs here?
11851184
@TODO consider if we ever need to access the original image directly!
1185+
@TODO every source image request requires joins for the deployment and data source, is this necessary?
11861186
"""
11871187
# Get presigned URL if access keys are configured
11881188
data_source = self.deployment.data_source if self.deployment and self.deployment.data_source else None

0 commit comments

Comments
 (0)