Skip to content

Commit 16a8b1e

Browse files
authored
Merge pull request #23 from Mosquito-Alert/add_review
Add review
2 parents 8ba77ee + 611e25d commit 16a8b1e

14 files changed

+392
-93
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"@websitebeaver/vue-magnifier": "^1.1.0",
2323
"dayjs": "^1.11.13",
2424
"exifr": "^7.1.3",
25-
"mosquito-alert": "github:mosquito-alert/mosquito-alert-typescript-sdk#0.1.21",
25+
"mosquito-alert": "github:mosquito-alert/mosquito-alert-typescript-sdk#0.1.23",
2626
"pinia": "^3.0.1",
2727
"primeicons": "^7.0.0",
2828
"primevue": "^4.3.3",

src/components/annotations/AnnotationNotAnInsectButton.vue

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const toast = useToast();
1818
const props = withDefaults(defineProps<{
1919
observation: Observation | AssignedObservation | SimplifiedObservationWithPhotos,
2020
loading?: boolean
21+
onConfirm?: () => Promise<void> | void
2122
}>(), {
2223
loading: false
2324
});
@@ -44,20 +45,28 @@ const confirmNotInsect = (event: MouseEvent) => {
4445
label: 'Accept',
4546
severity: 'danger'
4647
},
47-
accept: () => {
48-
const annotationRequest = <AnnotationRequest>{
49-
classification: null,
50-
}
51-
identificationTasksApi.annotationsCreate({
52-
observationUuid: props.observation.uuid,
53-
annotationRequest: annotationRequest,
54-
}).then(() => {
55-
toast.add({ severity: 'info', summary: 'Observation rejected', detail: 'Marked as not an insect', life: 3000 });
48+
accept: async () => {
49+
try {
50+
if (props.onConfirm) {
51+
await props.onConfirm();
52+
} else {
53+
const annotationRequest = <AnnotationRequest>{
54+
classification: null,
55+
}
56+
try {
57+
await identificationTasksApi.annotationsCreate({
58+
observationUuid: props.observation.uuid,
59+
annotationRequest: annotationRequest,
60+
});
61+
toast.add({ severity: 'info', summary: 'Observation rejected', detail: 'Marked as not an insect', life: 3000 });
62+
} catch {
63+
toast.add({ severity: 'danger', summary: 'Failed', detail: 'Annotation failed', life: 3000 });
64+
}
65+
}
5666
emit('onSubmitSuccess')
57-
}).catch(() => {
58-
toast.add({ severity: 'danger', summary: 'Failed', detail: 'Annotation failed', life: 3000 });
67+
} catch {
5968
emit('onSubmitFailure')
60-
})
69+
}
6170
},
6271
reject: () => { }
6372
});

src/components/identificationTasks/IdentificationTaskGrid.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="items-center justify-center grid grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
2+
<div class="items-center justify-center grid lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4">
33
<div v-for="(item, key) in tasks" :key="key" class="relative w-full h-full overflow-hidden group">
44
<IdentificationTaskGridItem :task="item" @on-review-success="$emit('onChange')" />
55
</div>

src/components/identificationTasks/IdentificationTaskGridItem.vue

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
</figcaption>
2626
<figcaption v-else-if="$can('add', 'Review') && !task.review"
2727
class="absolute bottom-0 left-0 p-2 flex gap-2 w-full bg-linear-to-t from-black to-transparent">
28-
<TaxonTreeSelect class="flex-1" placeholder="Select taxon" @on-change="(taxon) => submitReview(taxon)" />
29-
<AnnotationNotAnInsectButton :observation="task.observation" />
28+
<TaxonTreeSelect class="flex-1" placeholder="Select taxon"
29+
@on-change="(taxon) => taxon ? submitReview(taxon) : null" />
30+
<AnnotationNotAnInsectButton :observation="task.observation" @confirm="submitReview(null)" />
3031
</figcaption>
3132
</figure>
3233
</template>
@@ -36,15 +37,17 @@
3637
import { ref } from 'vue';
3738
import { useToast } from 'primevue/usetoast';
3839
39-
import type { IdentificationTask, Taxon, AnnotationRequest, AnnotationClassificationRequest } from 'mosquito-alert';
40-
import { AnnotationClassificationConfidenceLabel } from 'mosquito-alert';
40+
import type { IdentificationTask, Taxon, IdentificationTasksApiReviewCreateRequest } from 'mosquito-alert';
41+
import { AnnotationClassificationConfidenceLabel, CreateOverwriteReviewRequestAction } from 'mosquito-alert';
4142
4243
import CountryTag from '@/components/countries/CountryTag.vue';
4344
import AnnotationNotAnInsectButton from '@/components/annotations/AnnotationNotAnInsectButton.vue';
4445
import IdentificationTaskStatusTag from '@/components/identificationTasks/IdentificationTaskStatusTag.vue';
4546
import IdentificationTaskResultTag from '@/components/identificationTasks/IdentificationTaskResultTag.vue';
4647
import TaxonTreeSelect from '@/components/taxa/TaxonTreeSelect.vue';
4748
49+
import { getPublicNote } from '@/utils/AnnotationUtils';
50+
4851
import { identificationTasksApi } from '@/services/apiService';
4952
5053
const toast = useToast();
@@ -60,26 +63,23 @@ const emit = defineEmits<{
6063
6164
const isSubmittingReview = ref<boolean>(false);
6265
63-
const submitReview = (taxon: Taxon | undefined) => {
64-
if (!taxon) return;
65-
66+
const submitReview = (taxon: Taxon | null) => {
6667
isSubmittingReview.value = true;
67-
const annotationRequest = <AnnotationRequest>{
68-
best_photo_uuid: props.task.public_photo.uuid,
69-
classification: <AnnotationClassificationRequest>{
70-
taxon_id: taxon.id,
71-
confidence_label: AnnotationClassificationConfidenceLabel.Definitely
72-
},
73-
is_decisive: true,
74-
observation_flags: {
75-
is_visible: true
76-
},
77-
}
78-
identificationTasksApi.annotationsCreate({
68+
const request: IdentificationTasksApiReviewCreateRequest = {
7969
observationUuid: props.task.observation.uuid,
80-
annotationRequest: annotationRequest,
81-
}).then(() => {
82-
toast.add({ severity: 'success', summary: 'Success', detail: `Annotated as '${taxon.name}''`, life: 3000 });
70+
metaCreateIdentificationTaskReviewRequest: {
71+
action: CreateOverwriteReviewRequestAction.Overwrite,
72+
public_photo_uuid: props.task.public_photo.uuid,
73+
is_safe: taxon != null,
74+
public_note: taxon ? getPublicNote(taxon, true, 'en') : null, // TODO: get locale from user.
75+
result: taxon ? {
76+
taxon_id: taxon.id,
77+
confidence_label: AnnotationClassificationConfidenceLabel.Definitely
78+
} : null,
79+
}
80+
}
81+
identificationTasksApi.reviewCreate(request).then(() => {
82+
toast.add({ severity: 'success', summary: 'Success', detail: `Annotated as '${taxon ? taxon.name : "Not an insect"}'`, life: 3000 });
8383
emit('onReviewSuccess')
8484
}).catch(() => {
8585
toast.add({ severity: 'danger', summary: 'Failed', detail: 'Annotation failed', life: 3000 });
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<Select id="isSafe_select" :model-value="{ 'isSafe': isSafe }" @update:model-value="val => isSafe = val.isSafe"
3+
:options="options" optionLabel="isSafe" :disabled="disabled">
4+
<template #value="slotProps">
5+
<IdentificationTaskIsSafeTag :is-safe="slotProps.value.isSafe" />
6+
</template>
7+
<template #option="slotProps">
8+
<IdentificationTaskIsSafeTag :is-safe="slotProps.option.isSafe" />
9+
</template>
10+
</Select>
11+
</template>
12+
13+
<script setup lang="ts">
14+
import { ref } from 'vue';
15+
16+
import IdentificationTaskIsSafeTag from './IdentificationTaskIsSafeTag.vue';
17+
18+
const isSafe = defineModel<boolean>();
19+
20+
withDefaults(defineProps<{
21+
disabled?: boolean,
22+
}>(), {
23+
disabled: false
24+
});
25+
26+
const options = ref<Array<{ isSafe: boolean }>>([
27+
{ isSafe: true },
28+
{ isSafe: false },
29+
]);
30+
31+
</script>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<template>
2+
<Tag :icon="isSafe ? 'pi pi-shield' : 'pi pi-eye-slash'" :value="isSafe ? 'Safe content' : 'Hidden'"
3+
:severity="isSafe ? 'success' : 'danger'" />
4+
</template>
5+
6+
<script setup lang="ts">
7+
8+
defineProps<{
9+
isSafe: boolean;
10+
}>();
11+
12+
</script>

src/components/identificationTasks/IdentificationTaskResultTag.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<TaxonClassificationTag :classification="result || null">
2+
<TaxonClassificationTag :classification="result">
33
<div
44
class="flex absolute top-0 right-0 translate-x-1/2 -translate-y-1/2 origin-[100%_0%] m-0 outline-2 outline-solid outline-white text-surface-900 bg-white justify-items-center items-center p-1 rounded-full">
55
<i v-if='result.source === IdentificationTaskResultSource.Ai' class="pi pi-microchip-ai"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<Message :severity="review.action == IdentificationTaskReviewAction.Agree ? 'success' : 'warn'">
3+
<template #icon>
4+
<span class="material-symbols-outlined p-message-icon">
5+
rate_review
6+
</span>
7+
</template>
8+
<span>
9+
The reviewer has <strong>
10+
{{ review.action == IdentificationTaskReviewAction.Agree ? 'agreed' : 'disagreed' }}
11+
</strong>
12+
with the result.
13+
</span>
14+
</Message>
15+
</template>
16+
17+
<script setup lang="ts">
18+
import type { IdentificationTaskReview } from 'mosquito-alert';
19+
import { IdentificationTaskReviewAction } from 'mosquito-alert';
20+
21+
defineProps<{
22+
review: IdentificationTaskReview
23+
}>()
24+
25+
</script>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<Dialog :visible="visible" position='bottomright' header="Confirm identification result" :closable="false"
3+
:close-on-escape="false" :draggable="false">
4+
<span class="text-surface-500 dark:text-surface-400 block mb-8">
5+
Do you approve this identification task?
6+
</span>
7+
<div class="flex gap-2">
8+
<Button icon="pi pi-times" severity="danger" variant="outlined" label="Disagree" rounded :loading="loading"
9+
@click="$emit('disagree')" />
10+
<Button icon="pi pi-check" severity="success" label="Agree" rounded class="flex-1" :loading="loading"
11+
@click="$emit('agree')" />
12+
</div>
13+
</Dialog>
14+
</template>
15+
16+
<script setup lang="ts">
17+
18+
withDefaults(defineProps<{
19+
loading?: boolean
20+
visible?: boolean
21+
}>(), {
22+
loading: false,
23+
visible: true
24+
});
25+
26+
// Emits
27+
defineEmits<{
28+
(e: 'agree'): void
29+
(e: 'disagree'): void
30+
}>();
31+
</script>

0 commit comments

Comments
 (0)