Skip to content

Commit b7371df

Browse files
Merge branch 'main' into fix/upload-captures
2 parents 5c9dc9b + 78a0cb2 commit b7371df

File tree

5 files changed

+78
-93
lines changed

5 files changed

+78
-93
lines changed

ami/main/api/views.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,9 +1069,9 @@ def get_serializer_class(self):
10691069
else:
10701070
return OccurrenceSerializer
10711071

1072-
def get_queryset(self) -> QuerySet:
1072+
def get_queryset(self) -> QuerySet["Occurrence"]:
10731073
project = self.get_active_project()
1074-
qs = super().get_queryset()
1074+
qs = super().get_queryset().valid() # type: ignore
10751075
if project:
10761076
qs = qs.filter(project=project)
10771077
qs = qs.select_related(
@@ -1085,10 +1085,7 @@ def get_queryset(self) -> QuerySet:
10851085
if self.action == "list":
10861086
qs = (
10871087
qs.all()
1088-
.exclude(detections=None)
1089-
.exclude(event=None)
10901088
.filter(determination_score__gte=get_active_classification_threshold(self.request))
1091-
.exclude(first_appearance_timestamp=None) # This must come after annotations
10921089
.order_by("-determination_score")
10931090
)
10941091

@@ -1426,11 +1423,7 @@ def get(self, request):
14261423
"events_count": Event.objects.filter(deployment__project=project, deployment__isnull=False).count(),
14271424
"captures_count": SourceImage.objects.filter(deployment__project=project).count(),
14281425
# "detections_count": Detection.objects.filter(occurrence__project=project).count(),
1429-
"occurrences_count": Occurrence.objects.filter(
1430-
project=project,
1431-
# determination_score__gte=confidence_threshold,
1432-
event__isnull=False,
1433-
).count(),
1426+
"occurrences_count": Occurrence.objects.valid().filter(project=project).count(), # type: ignore
14341427
"taxa_count": Occurrence.objects.all().unique_taxa(project=project).count(), # type: ignore
14351428
}
14361429
else:
@@ -1440,10 +1433,7 @@ def get(self, request):
14401433
"events_count": Event.objects.filter(deployment__isnull=False).count(),
14411434
"captures_count": SourceImage.objects.count(),
14421435
# "detections_count": Detection.objects.count(),
1443-
"occurrences_count": Occurrence.objects.filter(
1444-
# determination_score__gte=confidence_threshold,
1445-
event__isnull=False
1446-
).count(),
1436+
"occurrences_count": Occurrence.objects.valid().count(), # type: ignore
14471437
"taxa_count": Occurrence.objects.all().unique_taxa().count(), # type: ignore
14481438
"last_updated": timezone.now(),
14491439
}

ami/main/models.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,11 +2188,14 @@ def __str__(self) -> str:
21882188
return f"#{self.pk} from SourceImage #{self.source_image_id} with Algorithm #{self.detection_algorithm_id}"
21892189

21902190

2191-
class OccurrenceQuerySet(models.QuerySet):
2192-
def with_detections_count(self) -> models.QuerySet:
2191+
class OccurrenceQuerySet(models.QuerySet["Occurrence"]):
2192+
def valid(self):
2193+
return self.exclude(detections__isnull=True)
2194+
2195+
def with_detections_count(self):
21932196
return self.annotate(detections_count=models.Count("detections", distinct=True))
21942197

2195-
def with_timestamps(self) -> models.QuerySet:
2198+
def with_timestamps(self):
21962199
"""
21972200
These are timestamps used for filtering and ordering in the UI.
21982201
"""
@@ -2206,14 +2209,14 @@ def with_timestamps(self) -> models.QuerySet:
22062209
),
22072210
)
22082211

2209-
def with_identifications(self) -> models.QuerySet:
2212+
def with_identifications(self):
22102213
return self.prefetch_related(
22112214
"identifications",
22122215
"identifications__taxon",
22132216
"identifications__user",
22142217
)
22152218

2216-
def unique_taxa(self, project: Project | None = None) -> models.QuerySet:
2219+
def unique_taxa(self, project: Project | None = None):
22172220
qs = self
22182221
if project:
22192222
qs = self.filter(project=project)
@@ -2225,12 +2228,16 @@ def unique_taxa(self, project: Project | None = None) -> models.QuerySet:
22252228
return qs
22262229

22272230

2228-
class OccurrenceManager(models.Manager):
2229-
def get_queryset(self) -> OccurrenceQuerySet:
2230-
return OccurrenceQuerySet(self.model, using=self._db).select_related(
2231-
"determination",
2232-
"deployment",
2233-
"project",
2231+
class OccurrenceManager(models.Manager.from_queryset(OccurrenceQuerySet)):
2232+
def get_queryset(self):
2233+
return (
2234+
super()
2235+
.get_queryset()
2236+
.select_related(
2237+
"determination",
2238+
"deployment",
2239+
"project",
2240+
)
22342241
)
22352242

22362243

@@ -2250,7 +2257,7 @@ class Occurrence(BaseModel):
22502257
detections: models.QuerySet[Detection]
22512258
identifications: models.QuerySet[Identification]
22522259

2253-
objects: OccurrenceManager = OccurrenceManager()
2260+
objects = OccurrenceManager()
22542261

22552262
def __str__(self) -> str:
22562263
name = f"Occurrence #{self.pk}"

ui/src/data-services/models/occurrence.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,19 @@ export class Occurrence {
120120
return this._occurrence.detections_count
121121
}
122122

123-
get sessionId(): string {
123+
get sessionId(): string | undefined {
124+
if (!this._occurrence.event) {
125+
return undefined
126+
}
127+
124128
return `${this._occurrence.event.id}`
125129
}
126130

127-
get sessionLabel(): string {
131+
get sessionLabel(): string | undefined {
132+
if (!this._occurrence.event) {
133+
return undefined
134+
}
135+
128136
return this._occurrence.event.name
129137
}
130138

ui/src/pages/occurrence-details/occurrence-details.tsx

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,22 @@ export const OccurrenceDetails = ({
6969
)
7070
.map((item) => ({
7171
...item,
72-
to: pathname.includes(
73-
APP_ROUTES.SESSIONS({ projectId: projectId as string })
74-
)
75-
? undefined
76-
: getAppRoute({
77-
to: APP_ROUTES.SESSION_DETAILS({
78-
projectId: projectId as string,
79-
sessionId: occurrence.sessionId,
72+
to:
73+
!occurrence.sessionId ||
74+
pathname.includes(
75+
APP_ROUTES.SESSIONS({ projectId: projectId as string })
76+
)
77+
? undefined
78+
: getAppRoute({
79+
to: APP_ROUTES.SESSION_DETAILS({
80+
projectId: projectId as string,
81+
sessionId: occurrence.sessionId,
82+
}),
83+
filters: {
84+
occurrence: occurrence.id,
85+
capture: item.captureId,
86+
},
8087
}),
81-
filters: {
82-
occurrence: occurrence.id,
83-
capture: item.captureId,
84-
},
85-
}),
8688
}))
8789
: [],
8890
[occurrence]
@@ -97,17 +99,19 @@ export const OccurrenceDetails = ({
9799
{
98100
label: translate(STRING.FIELD_LABEL_SESSION),
99101
value: occurrence.sessionLabel,
100-
to: pathname.includes(
101-
APP_ROUTES.SESSIONS({ projectId: projectId as string })
102-
)
103-
? undefined
104-
: getAppRoute({
105-
to: APP_ROUTES.SESSION_DETAILS({
106-
projectId: projectId as string,
107-
sessionId: occurrence.sessionId,
102+
to:
103+
!occurrence.sessionId ||
104+
pathname.includes(
105+
APP_ROUTES.SESSIONS({ projectId: projectId as string })
106+
)
107+
? undefined
108+
: getAppRoute({
109+
to: APP_ROUTES.SESSION_DETAILS({
110+
projectId: projectId as string,
111+
sessionId: occurrence.sessionId,
112+
}),
113+
filters: { occurrence: occurrence.id },
108114
}),
109-
filters: { occurrence: occurrence.id },
110-
}),
111115
},
112116
{
113117
label: translate(STRING.FIELD_LABEL_DATE),

ui/src/pages/occurrences/occurrence-columns.tsx

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -97,58 +97,34 @@ export const columns: (
9797
id: 'session',
9898
name: translate(STRING.FIELD_LABEL_SESSION),
9999
sortField: 'event',
100-
renderCell: (item: Occurrence) => (
101-
<Link
102-
to={APP_ROUTES.SESSION_DETAILS({
103-
projectId,
104-
sessionId: item.sessionId,
105-
})}
106-
>
107-
<BasicTableCell value={item.sessionLabel} theme={CellTheme.Primary} />
108-
</Link>
109-
),
100+
renderCell: (item: Occurrence) => {
101+
if (!item.sessionId) {
102+
return <></>
103+
}
104+
105+
return (
106+
<Link
107+
to={APP_ROUTES.SESSION_DETAILS({
108+
projectId,
109+
sessionId: item.sessionId,
110+
})}
111+
>
112+
<BasicTableCell value={item.sessionLabel} theme={CellTheme.Primary} />
113+
</Link>
114+
)
115+
},
110116
},
111117
{
112118
id: 'date',
113119
name: translate(STRING.FIELD_LABEL_DATE_OBSERVED),
114120
sortField: 'first_appearance_timestamp',
115-
renderCell: (item: Occurrence) => (
116-
<Link
117-
to={getAppRoute({
118-
to: APP_ROUTES.SESSION_DETAILS({
119-
projectId,
120-
sessionId: item.sessionId,
121-
}),
122-
filters: {
123-
occurrence: item.id,
124-
timestamp: item.firstAppearanceTimestamp,
125-
},
126-
})}
127-
>
128-
<BasicTableCell value={item.dateLabel} />
129-
</Link>
130-
),
121+
renderCell: (item: Occurrence) => <BasicTableCell value={item.dateLabel} />,
131122
},
132123
{
133124
id: 'time',
134125
sortField: 'first_appearance_time',
135126
name: translate(STRING.FIELD_LABEL_TIME_OBSERVED),
136-
renderCell: (item: Occurrence) => (
137-
<Link
138-
to={getAppRoute({
139-
to: APP_ROUTES.SESSION_DETAILS({
140-
projectId,
141-
sessionId: item.sessionId,
142-
}),
143-
filters: {
144-
occurrence: item.id,
145-
timestamp: item.firstAppearanceTimestamp,
146-
},
147-
})}
148-
>
149-
<BasicTableCell value={item.timeLabel} />
150-
</Link>
151-
),
127+
renderCell: (item: Occurrence) => <BasicTableCell value={item.timeLabel} />,
152128
},
153129
{
154130
id: 'duration',

0 commit comments

Comments
 (0)