diff --git a/django_api/models.py b/django_api/models.py index 44bbd764..ac1bf177 100644 --- a/django_api/models.py +++ b/django_api/models.py @@ -1,263 +1,258 @@ from django.db import models from django.contrib.auth.models import User import datetime from django.utils.translation import gettext as _ # Ref: database_model_20210421_MB.drawio 21.04.2021 class Country(models.Model): name = models.CharField(verbose_name="Country name", max_length=120, null=True) iso_code = models.CharField(max_length=3, null=True) def __str__(self): return f"{self.name}" class Meta: ordering = ('name',) class Language(models.Model): name = models.CharField(verbose_name="Language name", max_length=120, null=True) iso_code = models.CharField(max_length=3, null=True) def __str__(self): return f"{self.name}" class Meta: ordering = ('name',) class Oa(models.Model): status = models.CharField(max_length=1000, null=True) description = models.CharField(max_length=1000, null=True) subscription = models.BooleanField(default=False) accepted_manuscript = models.BooleanField(default=False) apc = models.BooleanField(default=False) final_version = models.BooleanField(default=False) def __str__(self): return f"{self.status}" class Meta: ordering = ('-subscription',) class Publisher(models.Model): name = models.CharField(verbose_name="Publisher name", max_length=1000, null=True) city = models.CharField(max_length=100, null=True) state = models.CharField(max_length=3, null=True) country = models.ManyToManyField("Country") starting_year = models.IntegerField(blank=True, null=True) website = models.URLField(max_length=1000) oa_policies = models.URLField(max_length=1000) def __str__(self): return f"{self.name}" class Meta: ordering = ('name',) class Issn(models.Model): PRINT = '1' ELECTRONIC = '2' OTHER = '3' TYPE_CHOICES = ( (PRINT, 'Print'), (ELECTRONIC, 'Electronic'), (OTHER, 'Other'), ) journal = models.ForeignKey("Journal", null=True, on_delete=models.CASCADE, related_name = "classIssn") #journal.classissn issn = models.CharField(max_length=9, null=False) issn_type = models.CharField( choices=TYPE_CHOICES, max_length=10, blank=True ) def __str__(self): return f"{self.issn} ({dict(self.TYPE_CHOICES)[self.issn_type]})" class Meta: ordering = ('issn',) class Journal(models.Model): name = models.CharField(verbose_name="Journal name", max_length=800, blank=True, null=True) # search journal with name name_short_iso_4 = models.CharField(max_length=300, blank=True, null=True) publisher = models.ManyToManyField(Publisher) website = models.URLField(max_length=300, blank=True, null=True) language = models.ManyToManyField(Language) # 2021-08-11: only one-to-many relationship between Journal and ISSN # issn = models.ForeignKey("Issn", null=True, on_delete=models.CASCADE) oa_options = models.URLField(max_length=1000, blank=True, null=True) oa_status = models.ForeignKey("Oa", on_delete=models.CASCADE) starting_year = models.IntegerField(blank=True, null=True) end_year = models.IntegerField(blank=True, null=True) doaj_seal = models.BooleanField(default=False) doaj_status = models.BooleanField(default=False) lockss = models.BooleanField(default=False) nlch = models.BooleanField(default=False) portico = models.BooleanField(default=False) qoam_av_score = models.DecimalField(decimal_places=2, max_digits=5, blank=True, null=True) def __str__(self): return f"{self.name} from {self.website}" class Meta: ordering = ('name',) class Organization(models.Model): name = models.CharField(verbose_name="Organization name", max_length=600, null=True) website = models.URLField(max_length=600, blank=True, null=True) country = models.ManyToManyField("Country") ror = models.CharField(max_length=255, blank=True, null=True) fundref = models.CharField(max_length=255, blank=True, null=True) starting_year = models.IntegerField(blank=True, null=True) is_funder = models.BooleanField(default=False) ir_name = models.CharField(verbose_name="Institutional repository name", max_length=40, null=True, blank=True) ir_url = models.URLField(verbose_name="Institutional repository URL", max_length=100, null=True, blank=True) def __str__(self): return f"{self.name}" class Meta: ordering = ('name',) class Version(models.Model): description = models.CharField(max_length=300, null=False) def __str__(self): return f"{self.description}" class Licence(models.Model): name_or_abbrev = models.CharField(max_length=300, null=False) website = models.URLField(max_length=600, null=True, blank=True) class Meta: ordering = ('name_or_abbrev',) def __str__(self): return f"{self.name_or_abbrev}" class Cost_factor_type(models.Model): name = models.CharField(max_length=300, null=False) def __str__(self): return f"{self.name}" class Cost_factor(models.Model): cost_factor_type = models.ForeignKey(Cost_factor_type, on_delete=models.CASCADE, blank=True, null=True) amount = models.IntegerField(null=False) symbol = models.CharField(max_length=10, null=False) comment = models.CharField(max_length=120, default="") class Meta: ordering = ('amount',) def __str__(self): return f"{self.amount} {self.symbol}" class Term(models.Model): version = models.ManyToManyField(Version) cost_factor = models.ManyToManyField(Cost_factor) embargo_months = models.IntegerField(blank=True, null=True) ir_archiving = models.BooleanField(default=False) licence = models.ManyToManyField(Licence) comment = models.CharField(max_length=600, null=True, blank=True) def __str__(self): try: # Maybe these fields should not allow NULL values? if self.embargo_months is None: embargo = 'no_' else: embargo = str(self.embargo_months) if self.comment is None: comment = '' else: comment = str(self.comment) - if self.source is None: - source = '' - else: - source = str(self.comment) - term_data = (str(self.id), ';'.join([str(x) for x in self.version.all()]), ';'.join([str(x) for x in self.licence.all()]), ';'.join([str(x) for x in self.cost_factor.all()]), f'Archiving{str(self.ir_archiving)} {embargo}months', comment,) return ' - '.join(term_data) except RecursionError: # The JSON import in the admin module somehow throws a ValueError during the loading process # probably due to incomplete information in the many2many relationships # Then the error log apparently triggers a cascade of errors until # the RecursionError level is hit. Falling back to a basic __str__ # for the RecursionError seems to bypass the problem. return f"[Term.__str__() error] {self.id} - {self.comment}" class Meta: ordering = ('-ir_archiving', 'embargo_months', 'comment') class ConditionType(models.Model): condition_issuer = models.CharField(max_length=300, null=False) def __str__(self): return f"{self.condition_issuer}" class ConditionSet(models.Model): condition_type = models.ForeignKey(ConditionType, on_delete=models.CASCADE, blank=True, null=True) organization = models.ManyToManyField( Organization, through='OrganizationCondition', through_fields=('condition_set', 'organization') ) journal = models.ManyToManyField( Journal, through='JournalCondition', through_fields=('condition_set', 'journal') ) term = models.ManyToManyField(Term) source = models.URLField(max_length=600, null=True, blank=True) comment = models.CharField(max_length=100, null=True, blank=True) def __str__(self): return f"{self.id} {self.condition_type}|{self.comment}" class Meta: ordering = ('-condition_type__pk','comment') class OrganizationCondition(models.Model): organization = models.ForeignKey(Organization, on_delete=models.CASCADE, blank=True, null=True) condition_set = models.ForeignKey(ConditionSet, on_delete=models.CASCADE, blank=True, null=True) valid_from = models.DateField(blank=True, null=True) valid_until = models.DateField(blank=True, null=True) def __str__(self): return f"{self.id} {self.organization}/{self.condition_set}" class JournalCondition(models.Model): journal = models.ForeignKey(Journal, on_delete=models.CASCADE, blank=True, null=True) condition_set = models.ForeignKey(ConditionSet, on_delete=models.CASCADE, blank=True, null=True) valid_from = models.DateField(blank=True, null=True) valid_until = models.DateField(blank=True, null=True) def __str__(self): return f"{self.id} {self.journal.name}/{self.condition_set}" diff --git a/django_api/urls.py b/django_api/urls.py index 0acf6f0a..e2b38e2e 100644 --- a/django_api/urls.py +++ b/django_api/urls.py @@ -1,40 +1,39 @@ from django.urls import path, re_path, include from django.conf.urls.static import static from django.conf import settings # from .views import JournalViewSet, InstitViewSet, FunderViewSet, ConditionSetViewSet, TermViewSet from .views import * from rest_framework import routers from rest_framework.schemas import get_schema_view router = routers.DefaultRouter() router.register(r'journal', JournalViewSet) router.register(r'organization', OrgaViewSet) router.register(r'funder', FunderViewSet) router.register(r'conditionset', ConditionSetViewSet) router.register(r'term', TermViewSet) #show table details in the API router.register(r'country', CountryViewSet) router.register(r'language', LanguageViewSet) router.register(r'issn', IssnViewSet) router.register(r'oa', OaViewSet) router.register(r'publisher', PublisherViewSet) router.register(r'version', VersionViewSet) router.register(r'licence', LicenceViewSet) router.register(r'cost_factor_type', Cost_factor_typeViewSet) router.register(r'cost_factor', Cost_factorViewSet) router.register(r'conditiontype', ConditionTypeViewSet) router.register(r'JournalCondition', JournalConditionViewSet) router.register(r'organizationCondition', OrganizationConditionViewSet) - urlpatterns = [ path('', include(router.urls)), path('openapi', get_schema_view( title="OACCT API", description="API of the Open Access Compliance Check Tool (OACCT)", version ="0.0.1" ), name='openapi-schema'), -] \ No newline at end of file +] diff --git a/django_api/views.py b/django_api/views.py index 1aebfc62..37254f51 100644 --- a/django_api/views.py +++ b/django_api/views.py @@ -1,215 +1,216 @@ from django.contrib.auth.models import AbstractUser from django.shortcuts import render from django.contrib.auth import authenticate, login, logout from django.shortcuts import render from django.http import HttpResponse, HttpResponseRedirect, Http404, JsonResponse from .models import * from .serializers import * from rest_framework import viewsets, filters, generics from rest_framework.authentication import BasicAuthentication from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework import status from rest_framework.decorators import api_view from rest_framework.response import Response from itertools import chain from django.db.models import Count from dj_rql.filter_cls import RQLFilterClass from urllib.parse import unquote from datetime import date class JournalViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] search_fields = ['name'] filter_backends = (filters.SearchFilter,) queryset = Journal.objects.all() serializer_class = JournalSerializer class OrgaViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = OrgaSerializer queryset = Organization.objects.filter( is_funder=False ) class ConditionSetFilters(RQLFilterClass): - #Frontend api request: - # http://127.0.0.1:8000/api/conditionset/?and(eq(journal.id,3);eq(organization.id,11);eq(condition_type.id,3)) + #Frontend api request examples: + # http://127.0.0.1:8000/api/conditionset/?and(eq(journalcondition.journal.id,3),eq(organizationcondition.organization.id,11),eq(condition_type.id,1)) + # http://127.0.0.1:8000/api/conditionset/?and(eq(journalcondition.journal.id,14),ne(condition_type.id,2),ge(journalcondition.valid_until,2021-08-20),le(journalcondition.valid_from,2021-08-20),ge(organizationcondition.valid_until,2021-08-20),le(organizationcondition.valid_from,2021-08-20)) MODEL = ConditionSet DISTINCT = True FILTERS = ( 'id', { 'namespace': 'journalcondition', 'filters': ['id', 'valid_from', 'valid_until', { 'namespace': 'journal', 'filters': ['id', ], } ], }, { 'namespace': 'organizationcondition', 'filters': ['id', 'valid_from', 'valid_until', { 'namespace': 'organization', 'filters': ['id', ] } ], }, { 'namespace': 'condition_type', 'filters': ['id', ], }, ) class ConditionSetViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] queryset = ConditionSet.objects.all() # queryset = ConditionSet.objects.values('term__version__description') serializer_class = ConditionSetSerializer # serializer_class = ConditionGroupedSerializer rql_filter_class = ConditionSetFilters #.objects.values('term__version.description') class FunderViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = OrgaSerializer queryset = Organization.objects.filter( is_funder=True ) class TermViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = TermSerializer queryset = Term.objects.all() class CountryViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = CountrySerializer queryset = Country.objects.all() class LanguageViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = LanguageSerializer queryset = Language.objects.all() class IssnViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = IssnSerializer queryset = Issn.objects.all() class OaViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = OaSerializer queryset = Oa.objects.all() class PublisherViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = PublisherSerializer queryset = Publisher.objects.all() class VersionViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = VersionSerializer queryset = Version.objects.all() class LicenceViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = LicenceSerializer queryset = Licence.objects.all() class Cost_factor_typeViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = Cost_factor_typeSerializer queryset = Cost_factor_type.objects.all() class Cost_factorViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = Cost_factorSerializer queryset = Cost_factor.objects.all() class ConditionTypeViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = ConditionTypeSerializer queryset = ConditionType.objects.all() class OrganizationConditionViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = OrganizationConditionSerializer queryset = OrganizationCondition.objects.all() class JournalConditionViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = JournalConditionSerializer queryset = JournalCondition.objects.all() class OrganizationConditionViewSet(viewsets.ModelViewSet): authentification_classes = (BasicAuthentication,) permission_classes = [IsAuthenticatedOrReadOnly] serializer_class = OrganizationConditionSerializer queryset = OrganizationCondition.objects.all() # Count number of different version # OrganizationCondition.objects.annotate(version_count=Count('condition_set__term__version')) # OrganizationCondition.objects # .values('condition_set__term__version') #what to group by # .annotate(version_count=Count('condition_set__term__version')) # what to aggregate # group by version and count # OrganizationCondition.objects.values('condition_set__term__version').annotate(version_count=Count('condition_set__term__version')) # source https://hakibenita.com/django-group-by-sql # https://docs.djangoproject.com/en/3.2/topics/db/aggregation/ # OrganizationCondition.objects.values('condition_set__term__version').filter(organization_id=1).annotate(version_count=Count('condition_set__term__version')) \ No newline at end of file