Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AMI-268] Enable comments field when suggesting ID #376

Merged
merged 3 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ami/jobs/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def setUp(self):
self.job = Job.objects.create(project=self.project, name="Test job", delay=0)
self.user = User.objects.create_user( # type: ignore
email="[email protected]",
is_staff=True,
)
self.factory = APIRequestFactory()

Expand Down Expand Up @@ -99,7 +100,6 @@ def test_run_job(self):
jobs_run_url = reverse_with_params("api:job-run", args=[self.job.pk], params={"no_async": True})
self.client.force_authenticate(user=self.user)
resp = self.client.post(jobs_run_url)
self.client.force_authenticate(user=None)
self.assertEqual(resp.status_code, 200)
data = resp.json()
self.assertEqual(data["id"], self.job.pk)
Expand Down
2 changes: 2 additions & 0 deletions ami/main/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ class Meta:
"withdrawn",
"agreed_with_identification_id",
"agreed_with_prediction_id",
"comment",
"created_at",
"updated_at",
]
Expand Down Expand Up @@ -944,6 +945,7 @@ class Meta:
"taxon",
"user",
"withdrawn",
"comment",
"created_at",
]

Expand Down
17 changes: 17 additions & 0 deletions ami/main/migrations/0030_identification_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2024-04-16 18:56

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("main", "0029_alter_deployment_device_and_more"),
]

operations = [
migrations.AddField(
model_name="identification",
name="comment",
field=models.TextField(blank=True),
),
]
5 changes: 5 additions & 0 deletions ami/main/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,7 @@ class Identification(BaseModel):
related_name="agreed_identifications",
)
score = 1.0 # Always 1 for humans, at this time
comment = models.TextField(blank=True)

class Meta:
ordering = [
Expand Down Expand Up @@ -1390,6 +1391,10 @@ class Classification(BaseModel):
)
# job = models.CharField(max_length=255, null=True)

# Type hints for auto-generated fields
taxon_id: int
algorithm_id: int

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

Expand Down
50 changes: 50 additions & 0 deletions ami/main/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.db import connection
from django.test import TestCase
from rest_framework.test import APIRequestFactory, APITestCase
from rich import print

from ami.main.models import (
Expand All @@ -17,6 +18,7 @@
TaxonRank,
group_images_into_events,
)
from ami.users.models import User


def setup_test_project(reuse=True) -> tuple[Project, Deployment]:
Expand Down Expand Up @@ -547,3 +549,51 @@ def test_taxon_detail(self):
response = self.client.get(f"/api/v2/taxa/{taxon.pk}/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["name"], taxon.name)


class TestIdentification(APITestCase):
def setUp(self) -> None:
project, deployment = setup_test_project()
create_taxa(project=project)
create_captures(deployment=deployment)
group_images_into_events(deployment=deployment)
create_occurrences(deployment=deployment, num=5)
self.project = project
self.user = User.objects.create_user( # type: ignore
email="[email protected]",
is_staff=True,
)
self.factory = APIRequestFactory()
self.client.force_authenticate(user=self.user)
return super().setUp()

def test_identification(self):
from ami.main.models import Identification, Taxon

"""
Post a new identification suggestion and check that it changed the occurrence's determination.
"""

suggest_id_endpoint = "/api/v2/identifications/"
taxa = Taxon.objects.filter(projects=self.project)
assert taxa.count() > 1

occurrence = Occurrence.objects.filter(project=self.project).exclude(determination=None)[0]
original_taxon = occurrence.determination
assert original_taxon is not None
new_taxon = Taxon.objects.exclude(pk=original_taxon.pk)[0]
comment = "Test identification comment"

response = self.client.post(
suggest_id_endpoint,
{
"occurrence_id": occurrence.pk,
"taxon_id": new_taxon.pk,
"comment": comment,
},
)
self.assertEqual(response.status_code, 201)
occurrence.refresh_from_db()
self.assertEqual(occurrence.determination, new_taxon)
identification = Identification.objects.get(pk=response.json()["id"])
self.assertEqual(identification.comment, comment)
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface IdentificationFieldValues {
}
occurrenceId: string
taxonId: string
comment?: string
}

const convertToServerFieldValues = (
Expand All @@ -20,6 +21,7 @@ const convertToServerFieldValues = (
agreed_with_prediction_id: fieldValues.agreeWith?.predictionId,
occurrence_id: fieldValues.occurrenceId,
taxon_id: fieldValues.taxonId,
comment: fieldValues.comment,
})

export const useCreateIdentification = (onSuccess?: () => void) => {
Expand Down
3 changes: 3 additions & 0 deletions ui/src/data-services/models/occurrence-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ export interface Identification {
id: string
overridden?: boolean
taxon: Taxon
comment?: string
userPermissions: UserPermission[]
createdAt: string
}

export interface HumanIdentification extends Identification {
comment: string
user: {
id: string
name: string
Expand Down Expand Up @@ -57,6 +59,7 @@ export class OccurrenceDetails extends Occurrence {
overridden,
taxon,
user: { id: `${i.user.id}`, name: i.user.name, image: i.user.image },
comment: i.comment,
userPermissions: i.user_permissions,
createdAt: i.created_at,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
.content {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
position: relative;
}
Expand All @@ -23,3 +22,10 @@
align-items: center;
justify-content: flex-end;
}

.comment {
display: block;
padding-top: 2px;
@include paragraph-small();
color: $color-neutral-600;
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export const IdentificationCard = ({
})
}
/>
<div className={styles.comment}>
{identification.comment}
</div>
</IdentificationSummary>
<div className={styles.actions}>
{showAgree && (
Expand Down
5 changes: 4 additions & 1 deletion ui/src/pages/occurrence-details/suggest-id/suggest-id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const SuggestId = ({
}: SuggestIdProps) => {
const { projectId } = useParams()
const [taxon, setTaxon] = useState<Taxon>()
const [comment, setComment] = useState("");
const { createIdentification, isLoading, error } =
useCreateIdentification(onCancel)
const formError = error ? parseServerError(error)?.message : undefined
Expand Down Expand Up @@ -66,7 +67,8 @@ export const SuggestId = ({
<Input
label={translate(STRING.FIELD_LABEL_COMMENT)}
name="comment"
disabled
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
<div className={styles.formActions}>
<Button label={translate(STRING.CANCEL)} onClick={onCancel} />
Expand All @@ -82,6 +84,7 @@ export const SuggestId = ({
createIdentification({
occurrenceId: occurrenceId,
taxonId: taxon.id,
comment: comment,
})
}}
/>
Expand Down