From 550c61dd4f10c496eb8a3ccd055b5f629a63c3d3 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Thu, 17 Jun 2021 22:12:27 +0200
Subject: [PATCH] Work on organizations API

---
 scipost_django/api/urls.py                    |  8 ++
 .../organizations/api/serializers.py          | 82 ++++++++++---------
 scipost_django/organizations/api/urls.py      | 27 +++---
 scipost_django/organizations/api/views.py     | 26 ++++--
 scipost_django/organizations/api/viewsets.py  | 54 ++++++++++++
 5 files changed, 138 insertions(+), 59 deletions(-)
 create mode 100644 scipost_django/organizations/api/viewsets.py

diff --git a/scipost_django/api/urls.py b/scipost_django/api/urls.py
index 05779e14f..b3fbee159 100644
--- a/scipost_django/api/urls.py
+++ b/scipost_django/api/urls.py
@@ -8,6 +8,7 @@ from django.urls import include, path
 from rest_framework import routers
 
 from journals.viewsets import PublicationViewSet
+from organizations.api.viewsets import OrganizationViewSet, OrganizationNAPViewSet
 from submissions.viewsets import SubmissionViewSet
 
 # Next two: old style, to be deprecated:
@@ -21,7 +22,14 @@ app_name = 'api'
 
 router = routers.SimpleRouter()
 
+# journals
 router.register('publications', PublicationViewSet)
+
+# organizations
+router.register('organizations', OrganizationViewSet)
+router.register('nap/organizations', OrganizationNAPViewSet)
+
+# submissions
 router.register('submissions', SubmissionViewSet)
 
 # Next two: old style, to be deprecated:
diff --git a/scipost_django/organizations/api/serializers.py b/scipost_django/organizations/api/serializers.py
index d93a1a56d..6864e040d 100644
--- a/scipost_django/organizations/api/serializers.py
+++ b/scipost_django/organizations/api/serializers.py
@@ -2,58 +2,62 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
+import datetime
+
 from rest_framework import serializers
 
 from ..models import Organization
 
-from journals.api.serializers import OrgPubFractionSerializer
-from journals.models import Journal, OrgPubFraction
-
 
-class OrganizationSerializer(serializers.HyperlinkedModelSerializer):
+class OrganizationSerializer(serializers.ModelSerializer):
+    url = serializers.HyperlinkedIdentityField(
+        view_name='organizations:organization_detail',
+        lookup_field='pk'
+    )
 
     class Meta:
         model = Organization
         fields = [
-            'name', 'name_original', 'acronym', 'country'
+            'url',
+            'orgtype',
+            'status',
+            'name',
+            'name_original',
+            'acronym',
+            'country',
+            'parent',
+            'superseded_by',
         ]
-        read_only_fields = [
-            'name', 'name_original', 'acronym', 'country'
+
+
+class OrganizationNAPSerializer(OrganizationSerializer):
+    nap = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Organization
+        fields = [
+            'url',
+            'orgtype',
+            'status',
+            'name',
+            'name_original',
+            'acronym',
+            'country',
+            'parent',
+            'superseded_by',
+            'nap'
         ]
 
+    def get_nap(self, obj):
+        per_year = {}
+        for year in range(datetime.date.today().year, 2015, -1):
+            per_year[year] = obj.get_publications(year=year).count()
+        return {
+            'total': obj.cf_nr_associated_publications,
+            'per_year': per_year
+        }
+
 
 class OrganizationBalanceSerializer(serializers.BaseSerializer):
     def to_representation(self, instance):
         return instance.get_balance_info()
-
-        # pubfractions = OrgPubFraction.objects.filter(organization=instance)
-        # pubyears = range(int(timezone.now().strftime('%Y')), 2015, -1)
-        # rep = {}
-        # cumulative_balance = 0
-        # for year in pubyears:
-        #     rep[str(year)] = {}
-        #     summed_expenditure = 0
-        #     rep[str(year)]['expenditures'] = {}
-        #     pfy = pubfractions.filter(publication__publication_date__year=year)
-        #     contribution = instance.total_subsidies_in_year(year)
-        #     rep[str(year)]['contribution'] = contribution
-        #     journal_labels = set([f.publication.get_journal().doi_label for f in pfy.all()])
-        #     for journal_label in journal_labels:
-        #         sumpf = pfy.filter(
-        #             publication__doi_label__istartswith=journal_label + '.'
-        #         ).aggregate(Sum('fraction'))['fraction__sum']
-        #         costperpaper = get_object_or_404(Journal,
-        #             doi_label=journal_label).cost_per_publication(year)
-        #         expenditure = int(costperpaper* sumpf)
-        #         if sumpf > 0:
-        #             rep[str(year)]['expenditures'][journal_label] = {
-        #                 'pubfractions': sumpf,
-        #                 'costperpaper': costperpaper,
-        #                 'expenditure': expenditure,
-        #             }
-        #         summed_expenditure += expenditure
-        #     rep[str(year)]['expenditures']['total'] = summed_expenditure
-        #     rep[str(year)]['balance'] = contribution - summed_expenditure
-        #     cumulative_balance += contribution - summed_expenditure
-        # rep['cumulative'] = cumulative_balance
-        # return rep
diff --git a/scipost_django/organizations/api/urls.py b/scipost_django/organizations/api/urls.py
index 83e5a3669..1b9442509 100644
--- a/scipost_django/organizations/api/urls.py
+++ b/scipost_django/organizations/api/urls.py
@@ -4,21 +4,24 @@ __license__ = "AGPL v3"
 
 from django.urls import path
 
-from organizations.api import views as api_views
+from rest_framework import routers
 
+#from organizations.api import views as api_views
+from . import views as api_views
+from . import viewsets as api_viewsets
 
-urlpatterns = [
 
-    path( # /api/organizations/
-        '',
-        api_views.OrganizationListAPIView.as_view(),
-        name='organizations'
-    ),
-    path( # /api/organizations/<int:pk>
-        '<int:pk>',
-        api_views.OrganizationRetrieveAPIView.as_view(),
-        name='organization-detail'
-    ),
+router = routers.SimpleRouter()
+
+# OrganizationNAPViewSet before OrganizationViewSet, to prevent 404
+router.register('nap', api_viewsets.OrganizationNAPViewSet)
+router.register('', api_viewsets.OrganizationViewSet)
+
+
+urlpatterns = router.urls
+
+urlpatterns += [
+
     path( # /api/organizations/<int:pk>/balance
         '<int:pk>/balance',
         api_views.OrganizationBalanceAPIView.as_view(),
diff --git a/scipost_django/organizations/api/views.py b/scipost_django/organizations/api/views.py
index 0471fa8ba..2dfc6d549 100644
--- a/scipost_django/organizations/api/views.py
+++ b/scipost_django/organizations/api/views.py
@@ -3,19 +3,29 @@ __license__ = "AGPL v3"
 
 
 from rest_framework.generics import ListAPIView, RetrieveAPIView
+from rest_framework.settings import api_settings
+from rest_framework_csv import renderers as r
 
 from ..models import Organization
-from .serializers import OrganizationSerializer, OrganizationBalanceSerializer
+from .serializers import (
+    OrganizationSerializer,
+    OrganizationNAPSerializer,
+    OrganizationBalanceSerializer
+)
+from .viewsets import OrganizationFilterSet
 
 
-class OrganizationListAPIView(ListAPIView):
-    queryset = Organization.objects.all().order_by('name')
-    serializer_class = OrganizationSerializer
-
-
-class OrganizationRetrieveAPIView(RetrieveAPIView):
+class OrganizationNAPListAPIView(ListAPIView):
     queryset = Organization.objects.all()
-    serializer_class = OrganizationSerializer
+    serializer_class = OrganizationNAPSerializer
+    renderer_classes = tuple(api_settings.DEFAULT_RENDERER_CLASSES) + (r.PaginatedCSVRenderer, )
+    search_fields = ['name', 'name_original', 'acronym',]
+    filterset_class = OrganizationFilterSet
+    default_filtering_fields = [
+        'name__icontains',
+        'name_original__icontains',
+        'acronym__icontains'
+    ]
 
 
 class OrganizationBalanceAPIView(RetrieveAPIView):
diff --git a/scipost_django/organizations/api/viewsets.py b/scipost_django/organizations/api/viewsets.py
new file mode 100644
index 000000000..839e4ae98
--- /dev/null
+++ b/scipost_django/organizations/api/viewsets.py
@@ -0,0 +1,54 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+import datetime
+
+from django_filters import rest_framework as df_filters
+
+from rest_framework import viewsets
+from rest_framework.decorators import action
+from rest_framework.permissions import AllowAny
+from rest_framework.response import Response
+from rest_framework.settings import api_settings
+from rest_framework_csv import renderers as r
+
+from api.viewsets.mixins import FilteringOptionsActionMixin
+
+from ..models import Organization
+from .serializers import OrganizationSerializer, OrganizationNAPSerializer
+
+
+class OrganizationFilterSet(df_filters.FilterSet):
+    class Meta:
+        model = Organization
+        fields = {
+            'name': ['icontains',],
+            'name_original': ['icontains',],
+            'acronym': ['icontains',],
+            'country': ['exact',],
+            'cf_nr_associated_publications': ['gte', 'lte',]
+        }
+
+
+class OrganizationViewSet(FilteringOptionsActionMixin,
+                             viewsets.ReadOnlyModelViewSet):
+    queryset = Organization.objects.all()
+    permission_classes = [AllowAny,]
+    renderer_classes = tuple(api_settings.DEFAULT_RENDERER_CLASSES) + (r.PaginatedCSVRenderer, )
+    serializer_class = OrganizationSerializer
+    search_fields = ['name', 'name_original', 'acronym',]
+    filterset_class = OrganizationFilterSet
+    default_filtering_fields = [
+        'name__icontains',
+        'name_original__icontains',
+        'acronym__icontains'
+    ]
+
+
+class OrganizationNAPViewSet(OrganizationViewSet):
+    serializer_class = OrganizationNAPSerializer
+    ordering_fields = ['name', 'cf_nr_associated_publications']
+
+    def get_view_name(self):
+        return 'Organization NAP'
-- 
GitLab