diff --git a/dandiapi/api/views/dandiset.py b/dandiapi/api/views/dandiset.py index 1cf6e0650..c4de00d70 100644 --- a/dandiapi/api/views/dandiset.py +++ b/dandiapi/api/views/dandiset.py @@ -26,6 +26,7 @@ from dandiapi.api.asset_paths import get_root_paths_many from dandiapi.api.mail import send_ownership_change_emails from dandiapi.api.models import Dandiset, Version +from dandiapi.api.models.dandiset import DandisetStar from dandiapi.api.services import audit from dandiapi.api.services.dandiset import ( create_dandiset, @@ -196,6 +197,36 @@ def get_object(self): return dandiset + def _get_dandiset_star_context(self, dandisets): + # Default value for all relevant dandisets + dandisets_to_stars = { + d.id: {'total': 0, 'starred_by_current_user': False} for d in dandisets + } + + # Group the stars for these dandisets by the dandiset ID, + # yielding pairs of (Dandiset ID, Star Count) + dandiset_stars = ( + DandisetStar.objects.filter(dandiset__in=dandisets) + .values_list('dandiset') + .annotate(star_count=Count('id')) + .order_by() + ) + for dandiset_id, star_count in dandiset_stars: + dandisets_to_stars[dandiset_id]['total'] = star_count + + # Only annotate dandisets as starred by current user if user is logged in + user = self.request.user + if user.is_anonymous: + return dandisets_to_stars + user = typing.cast(User, user) + + # Filter previous query to current user stars + user_starred_dandisets = dandiset_stars.filter(user=user) + for dandiset_id, _ in user_starred_dandisets: + dandisets_to_stars[dandiset_id]['starred_by_current_user'] = True + + return dandisets_to_stars + @staticmethod def _get_dandiset_to_version_map(dandisets): """Map Dandiset IDs to that dandiset's draft and most recently published version.""" @@ -299,8 +330,14 @@ def list(self, request, *args, **kwargs): qs = self.get_queryset() dandisets = self.paginate_queryset(self.filter_queryset(qs)) dandisets_to_versions = self._get_dandiset_to_version_map(dandisets) + dandiset_stars = self._get_dandiset_star_context(dandisets) serializer = DandisetListSerializer( - dandisets, many=True, context={'dandisets': dandisets_to_versions} + dandisets, + many=True, + context={ + 'dandisets': dandisets_to_versions, + 'stars': dandiset_stars, + }, ) return self.get_paginated_response(serializer.data) @@ -565,7 +602,13 @@ def starred(self, request) -> Response: dandisets = Dandiset.objects.filter(stars__user=request.user).order_by('-stars__created') dandisets = self.paginate_queryset(dandisets) dandisets_to_versions = self._get_dandiset_to_version_map(dandisets) + dandiset_stars = self._get_dandiset_star_context(dandisets) serializer = DandisetListSerializer( - dandisets, many=True, context={'dandisets': dandisets_to_versions} + dandisets, + many=True, + context={ + 'dandisets': dandisets_to_versions, + 'stars': dandiset_stars, + }, ) return self.get_paginated_response(serializer.data) diff --git a/dandiapi/api/views/serializers.py b/dandiapi/api/views/serializers.py index a410f4c27..e8e15665f 100644 --- a/dandiapi/api/views/serializers.py +++ b/dandiapi/api/views/serializers.py @@ -169,6 +169,12 @@ def get_contact_person(self, dandiset): return contact + def get_star_count(self, dandiset): + return self.context['stars'][dandiset.id]['total'] + + def get_is_starred(self, dandiset): + return self.context['stars'][dandiset.id]['starred_by_current_user'] + most_recent_published_version = serializers.SerializerMethodField() draft_version = serializers.SerializerMethodField()