Skip to content

Commit 9e359af

Browse files
committed
Added is_favourite field to annotations
1 parent 2743c67 commit 9e359af

File tree

7 files changed

+118
-4
lines changed

7 files changed

+118
-4
lines changed

api/filters.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from django_filters import rest_framework as filters
66

7-
from tigacrafting.models import IdentificationTask, ExpertReportAnnotation, Taxon
7+
from tigacrafting.models import IdentificationTask, ExpertReportAnnotation, Taxon, FavoritedReports
88
from tigaserver_app.models import Report, Notification, OWCampaigns, EuropeCountry, Photo
99

1010
User = get_user_model()
@@ -247,6 +247,22 @@ def filter_by_is_decisive(self, queryset, name, value):
247247
)
248248
)
249249

250+
is_favourite = filters.BooleanFilter(
251+
method="filter_by_is_favourite"
252+
)
253+
254+
def filter_by_is_favourite(self, queryset, name, value):
255+
if not value:
256+
return queryset
257+
return queryset.annotate(
258+
is_favourite=models.Exists(
259+
FavoritedReports.objects.filter(
260+
user=models.OuterRef('user'),
261+
report=models.OuterRef('report'),
262+
)
263+
)
264+
).filter(is_favourite=value)
265+
250266
order_by = filters.OrderingFilter(
251267
fields=(("created", "created_at"), ("last_modified", "updated_at"))
252268
)

api/serializers.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
Taxon,
1919
ExpertReportAnnotation,
2020
UserStat,
21-
PhotoPrediction
21+
PhotoPrediction,
22+
FavoritedReports
2223
)
2324
from tigaserver_app.models import (
2425
NotificationContent,
@@ -815,12 +816,20 @@ class Meta:
815816
default=False,
816817
)
817818

819+
is_favourite = WritableSerializerMethodField(
820+
field_class=serializers.BooleanField,
821+
default=False,
822+
)
823+
818824
def get_is_flagged(self, obj) -> bool:
819825
return obj.status == ExpertReportAnnotation.STATUS_FLAGGED
820826

821827
def get_is_decisive(self, obj) -> bool:
822828
return obj.validation_complete_executive or obj.revise
823829

830+
def get_is_favourite(self, obj) -> bool:
831+
return obj.is_favourite
832+
824833
def validate(self, data):
825834
data['user'] = data.pop('user_hidden_obj')
826835
data['validation_complete'] = True
@@ -847,6 +856,33 @@ def validate(self, data):
847856

848857
return data
849858

859+
def create(self, validated_data):
860+
is_favourite = validated_data.pop("is_favourite", False)
861+
with transaction.atomic():
862+
instance = super().create(validated_data)
863+
if is_favourite:
864+
FavoritedReports.objects.get_or_create(
865+
user=instance.user,
866+
report=instance.report,
867+
)
868+
return instance
869+
870+
def update(self, instance, validated_data):
871+
is_favourite = validated_data.pop("is_favourite", False)
872+
with transaction.atomic():
873+
instance = super().update(instance, validated_data)
874+
if is_favourite:
875+
FavoritedReports.objects.get_or_create(
876+
user=instance.user,
877+
report=instance.report,
878+
)
879+
else:
880+
FavoritedReports.objects.filter(
881+
user=instance.user,
882+
report=instance.report,
883+
).delete()
884+
return instance
885+
850886
class Meta:
851887
model = ExpertReportAnnotation
852888
fields = (
@@ -860,6 +896,7 @@ class Meta:
860896
"feedback",
861897
"is_flagged",
862898
"is_decisive",
899+
"is_favourite",
863900
"tags",
864901
"created_at",
865902
"updated_at",

api/tests/integration/identification_tasks/annotations/create.tavern.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ stages:
9191
user_note: null
9292
is_flagged: false
9393
is_decisive: false
94+
is_favourite: false
9495
tags: !anylist
9596
created_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z
9697
updated_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z
@@ -153,6 +154,7 @@ stages:
153154
user_note: null
154155
is_flagged: false
155156
is_decisive: false
157+
is_favourite: false
156158
tags: !anylist
157159
created_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z
158160
updated_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z

api/tests/integration/identification_tasks/annotations/schema.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ variables:
2121
user_note: !anything
2222
is_flagged: !anybool
2323
is_decisive: !anybool
24+
is_favourite: !anybool
2425
created_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z
2526
updated_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z
2627
tags: !anylist

api/tests/test_views.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from rest_framework_simplejwt.settings import api_settings
1717

18-
from tigacrafting.models import ExpertReportAnnotation, IdentificationTask, PhotoPrediction
18+
from tigacrafting.models import ExpertReportAnnotation, IdentificationTask, PhotoPrediction, FavoritedReports
1919
from tigaserver_app.models import TigaUser, Report, Device, MobileApp
2020

2121
from api.tests.clients import AppAPIClient
@@ -796,6 +796,53 @@ def test_is_flagged_sets_status_to_flagged(self, api_client, endpoint, common_po
796796
annotation = ExpertReportAnnotation.objects.get(pk=response.data['id'])
797797
assert annotation.status == expected_result
798798

799+
@pytest.mark.parametrize(
800+
"is_favourite",
801+
[True, False]
802+
)
803+
def test_is_favourite_creates_FavoritedReports(self, api_client, endpoint, common_post_data, with_add_permission, is_favourite):
804+
post_data = common_post_data
805+
post_data['is_favourite'] = is_favourite
806+
807+
response = api_client.post(
808+
endpoint,
809+
data=post_data,
810+
format='json'
811+
)
812+
assert response.status_code == status.HTTP_201_CREATED
813+
assert response.data['is_favourite'] == is_favourite
814+
815+
annotation = ExpertReportAnnotation.objects.get(pk=response.data['id'])
816+
assert annotation.is_favourite == is_favourite
817+
assert FavoritedReports.objects.filter(
818+
report=annotation.identification_task.report,
819+
user=annotation.user
820+
).exists() == is_favourite
821+
822+
def test_is_favourite_does_nothing_if_already_exists_FavoritedReports(self, api_client, user, identification_task, endpoint, common_post_data, with_add_permission):
823+
FavoritedReports.objects.create(
824+
report=identification_task.report,
825+
user=user
826+
)
827+
828+
post_data = common_post_data
829+
post_data['is_favourite'] = True
830+
831+
response = api_client.post(
832+
endpoint,
833+
data=post_data,
834+
format='json'
835+
)
836+
assert response.status_code == status.HTTP_201_CREATED
837+
assert response.data['is_favourite'] is True
838+
839+
annotation = ExpertReportAnnotation.objects.get(pk=response.data['id'])
840+
assert annotation.is_favourite
841+
assert FavoritedReports.objects.filter(
842+
report=annotation.identification_task.report,
843+
user=annotation.user
844+
).exists()
845+
799846
@pytest.mark.parametrize(
800847
"pre_assign",
801848
[True, False]

api/views.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from rest_framework.viewsets import ReadOnlyModelViewSet
3232
from rest_framework_simplejwt.tokens import Token
3333

34-
from tigacrafting.models import IdentificationTask, ExpertReportAnnotation, Taxon, PhotoPrediction
34+
from tigacrafting.models import IdentificationTask, ExpertReportAnnotation, Taxon, PhotoPrediction, FavoritedReports
3535
from tigaserver_app.models import (
3636
TigaUser,
3737
EuropeCountry,
@@ -601,6 +601,13 @@ class AnnotationViewSet(NestedViewSetMixin, ListModelMixin, RetrieveModelMixin,
601601
'report',
602602
'best_photo',
603603
'taxon',
604+
).annotate(
605+
is_favourite=models.Exists(
606+
FavoritedReports.objects.filter(
607+
user=models.OuterRef('user'),
608+
report=models.OuterRef('report')
609+
)
610+
)
604611
)
605612
serializer_class = AnnotationSerializer
606613
filterset_class = AnnotationFilter

tigacrafting/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,10 @@ def is_high_confidence(self) -> bool:
865865
return False
866866
return get_is_high_confidence(value=self.confidence)
867867

868+
@cached_property
869+
def is_favourite(self) -> bool:
870+
return FavoritedReports.objects.filter(user=self.user, report=self.report).exists()
871+
868872
@classmethod
869873
def _get_auto_message(cls, category: 'Categories', validation_value: int, locale: str = 'en') -> Optional[str]:
870874
msg_dict = other_insect_msg_dict

0 commit comments

Comments
 (0)