Skip to content

Commit

Permalink
Merge pull request #334 from RolnickLab/feat/precalculate-values
Browse files Browse the repository at this point in the history
Options for project visibility, speed up session views
  • Loading branch information
mihow authored Dec 11, 2023
2 parents fd1ad95 + 88102f7 commit 1f26335
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 22 deletions.
2 changes: 2 additions & 0 deletions ami/main/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class BlogPostAdmin(admin.ModelAdmin[BlogPost]):
class ProjectAdmin(admin.ModelAdmin[Project]):
"""Admin panel example for ``Project`` model."""

list_display = ("name", "priority", "active", "created_at", "updated_at")


@admin.register(Deployment)
class DeploymentAdmin(admin.ModelAdmin[Deployment]):
Expand Down
63 changes: 41 additions & 22 deletions ami/main/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class ProjectViewSet(DefaultViewSet):
API endpoint that allows projects to be viewed or edited.
"""

queryset = Project.objects.prefetch_related("deployments").all()
queryset = Project.objects.filter(active=True).prefetch_related("deployments").all()
serializer_class = ProjectSerializer

def get_serializer_class(self):
Expand Down Expand Up @@ -149,17 +149,7 @@ class EventViewSet(DefaultViewSet):
API endpoint that allows events to be viewed or edited.
"""

queryset = (
Event.objects.select_related("deployment")
.annotate(
captures_count=models.Count("captures", distinct=True),
detections_count=models.Count("captures__detections", distinct=True),
occurrences_count=models.Count("occurrences", distinct=True),
taxa_count=models.Count("occurrences__determination", distinct=True),
duration=models.F("end") - models.F("start"),
)
.select_related("deployment", "project")
) # .prefetch_related("captures").all()
queryset = Event.objects.all()
serializer_class = EventSerializer
filterset_fields = ["deployment", "project"]
ordering_fields = [
Expand All @@ -184,6 +174,21 @@ def get_serializer_class(self):
else:
return EventSerializer

def get_queryset(self) -> QuerySet:
qs: QuerySet = super().get_queryset()
qs = qs.annotate(
captures_count=models.Count("captures", distinct=True),
duration=models.F("end") - models.F("start"),
).select_related("deployment", "project")

if self.action != "list":
qs = qs.annotate(
# detections_count=models.Count("captures__detections", distinct=True),
occurrences_count=models.Count("occurrences", distinct=True),
taxa_count=models.Count("occurrences__determination", distinct=True),
).prefetch_related("occurrences", "occurrences__determination")
return qs


class SourceImageViewSet(DefaultViewSet):
"""
Expand Down Expand Up @@ -538,7 +543,7 @@ def get_serializer_class(self):
else:
return TaxonSerializer

def filter_by_occurrence(self, queryset: QuerySet) -> QuerySet:
def filter_by_occurrence(self, queryset: QuerySet) -> tuple[QuerySet, bool]:
"""
Filter taxa by when/where it has occurred.
Expand All @@ -552,10 +557,12 @@ def filter_by_occurrence(self, queryset: QuerySet) -> QuerySet:
deployment_id = self.request.query_params.get("deployment")
event_id = self.request.query_params.get("event")

filter_active = any([occurrence_id, project_id, deployment_id, event_id])

if occurrence_id:
occurrence = Occurrence.objects.get(id=occurrence_id)
# This query does not need the same filtering as the others
return queryset.filter(occurrences=occurrence).distinct()
return queryset.filter(occurrences=occurrence).distinct(), True
elif project_id:
project = Project.objects.get(id=project_id)
queryset = super().get_queryset().filter(occurrences__project=project)
Expand All @@ -576,23 +583,35 @@ def filter_by_occurrence(self, queryset: QuerySet) -> QuerySet:
if not self.request.query_params.get("ordering"):
queryset = queryset.order_by("-best_determination_score")

return queryset
return queryset, filter_active

def get_queryset(self) -> QuerySet:
qs = super().get_queryset()
try:
qs = self.filter_by_occurrence(qs)
qs, filter_active = self.filter_by_occurrence(qs)
except exceptions.ObjectDoesNotExist as e:
from rest_framework.exceptions import NotFound

raise NotFound(detail=str(e))
qs = qs.select_related("parent", "parent__parent")
qs = qs.prefetch_related("occurrences")
qs = qs.annotate(
occurrences_count=models.Count("occurrences", distinct=True),
events_count=models.Count("occurrences__event", distinct=True),
last_detected=models.Max("classifications__detection__timestamp"),
)

if filter_active:
qs = qs.prefetch_related("occurrences")
qs = qs.annotate(
occurrences_count=models.Count("occurrences", distinct=True),
# events_count=models.Count("occurrences__event", distinct=True),
last_detected=models.Max("classifications__detection__timestamp"),
)
elif self.action == "list":
# If no filter don't return anything related to occurrences
# @TODO add a project_id filter to all request from the frontend
# event detail views should be filtered by project
qs = qs.prefetch_related(Prefetch("occurrences", queryset=Occurrence.objects.none()))
qs = qs.annotate(
occurrences_count=models.Value(0),
# events_count=models.Value(0),
last_detected=models.Value(None, output_field=models.DateTimeField()),
)

return qs

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.2 on 2023-12-03 23:36

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("main", "0027_update_occurrence_scores"),
]

operations = [
migrations.AlterModelOptions(
name="occurrence",
options={"ordering": ["-determination_score"]},
),
migrations.AlterModelOptions(
name="project",
options={"ordering": ["-priority", "created_at"]},
),
migrations.AddField(
model_name="project",
name="active",
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name="project",
name="priority",
field=models.IntegerField(default=1),
),
]
6 changes: 6 additions & 0 deletions ami/main/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ class Project(BaseModel):
taxa: models.QuerySet["Taxon"]
taxa_lists: models.QuerySet["TaxaList"]

active = models.BooleanField(default=True)
priority = models.IntegerField(default=1)

def deployments_count(self) -> int:
return self.deployments.count()

Expand All @@ -117,6 +120,9 @@ def summary_data(self):

return plots

class Meta:
ordering = ["-priority", "created_at"]


@final
class Device(BaseModel):
Expand Down

0 comments on commit 1f26335

Please sign in to comment.