diff --git a/django_api/migrations/0077_auto_20211208_1209.py b/django_api/migrations/0077_auto_20211208_1209.py new file mode 100644 index 00000000..f202958b --- /dev/null +++ b/django_api/migrations/0077_auto_20211208_1209.py @@ -0,0 +1,47 @@ +# Generated by Django 3.2.6 on 2021-12-08 11:09 + +from django.db import migrations, models +import django.db.models.deletion +import django_api.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_api', '0076_alter_journal_oa_status'), + ] + + operations = [ + migrations.CreateModel( + name='ConditionSubType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.CharField(max_length=300)), + ], + ), + migrations.AlterModelOptions( + name='conditionset', + options={'ordering': ('condition_type__pk', 'comment')}, + ), + migrations.AlterModelOptions( + name='country', + options={'ordering': ('name',), 'verbose_name_plural': 'Countries'}, + ), + migrations.AlterModelOptions( + name='journalcondition', + options={'verbose_name': 'Journal/condition_set relationship'}, + ), + migrations.AlterModelOptions( + name='oa', + options={'ordering': ('-subscription',), 'verbose_name': 'Open Access status', 'verbose_name_plural': 'Open Access statuses'}, + ), + migrations.AlterModelOptions( + name='organizationcondition', + options={'verbose_name': 'Organization/condition_set relationship'}, + ), + migrations.AddField( + model_name='conditionset', + name='subtype', + field=models.ForeignKey(default=django_api.models.ConditionSubType.get_default_pk, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_api.conditionsubtype'), + ), + ] diff --git a/django_api/models.py b/django_api/models.py index 6bcc77f7..6458371f 100644 --- a/django_api/models.py +++ b/django_api/models.py @@ -1,266 +1,280 @@ 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: verbose_name_plural = 'Countries' 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',) verbose_name = "Open Access status" verbose_name_plural = "Open Access statuses" 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", related_name ="oa_status", on_delete=models.CASCADE, null=True) 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.id} - {self.amount} {self.symbol} - {self.comment}" class Term(models.Model): version = models.ManyToManyField(Version) cost_factor = models.ManyToManyField(Cost_factor) licence = models.ManyToManyField(Licence) embargo_months = models.IntegerField(blank=True, null=True) ir_archiving = models.BooleanField(default=False) 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) 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 ConditionSubType(models.Model): + label = models.CharField(max_length=300, null=False) + + def __str__(self): + return f"{self.label}" + + @classmethod + def get_default_pk(cls): + condition_subtype, created = cls.objects.get_or_create(label='Automatic') + return condition_subtype.pk + + class ConditionSet(models.Model): condition_type = models.ForeignKey(ConditionType, on_delete=models.CASCADE, blank=True, null=True) + subtype = models.ForeignKey(ConditionSubType, on_delete=models.CASCADE, + default=ConditionSubType.get_default_pk, 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') + 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) class Meta: verbose_name = "Organization/condition_set relationship" def __str__(self): return f"{self.id} {self.organization.name}/ConditionSet {self.condition_set.id}" 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) class Meta: verbose_name = "Journal/condition_set relationship" def __str__(self): return f"{self.id} {self.journal.name}/{self.condition_set}" diff --git a/django_api/serializers.py b/django_api/serializers.py index 610ec76b..7c784379 100644 --- a/django_api/serializers.py +++ b/django_api/serializers.py @@ -1,246 +1,260 @@ from rest_framework import serializers from dj_rql.drf.serializers import RQLMixin from .models import * from drf_writable_nested.serializers import WritableNestedModelSerializer class CountrySerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) name = serializers.CharField(required=False) iso_code = serializers.CharField(required=False) class Meta: model = Country fields = '__all__' depth = 4 class LanguageSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) name = serializers.CharField(required=False) iso_code = serializers.CharField(required=False) class Meta: model = Language fields = '__all__' depth = 4 class PublisherSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) country = CountrySerializer(required=False, many=True) class Meta: model = Publisher fields = '__all__' depth = 4 class OaSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False, allow_null=True) description = serializers.CharField(required=False, allow_null=True) subscription = serializers.BooleanField(required=False) accepted_manuscript = serializers.BooleanField(required=False) apc = serializers.BooleanField(required=False) final_version = serializers.BooleanField(required=False) class Meta: model = Oa fields = '__all__' depth = 4 class IssnSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) class Meta: model = Issn fields = '__all__' depth = 1 class JournalSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) issn = IssnSerializer(required=False, source='classIssn', many=True) publisher = PublisherSerializer(required=False, many=True) language = LanguageSerializer(required=False, many=True) # allow update via post request --> "oa_status": {2}, # oa_status = serializers.PrimaryKeyRelatedField(queryset=Oa.objects.all()) oa_status = OaSerializer(required=False,allow_null=True) class Meta: model = Journal fields = '__all__' depth = 4 class LicenceSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) name_or_abbrev = serializers.CharField() website = serializers.URLField(allow_null=True, required=False) class Meta: model = Licence fields = '__all__' depth = 4 class Cost_factor_typeSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) name = serializers.CharField() class Meta: model = Cost_factor_type fields = '__all__' depth = 4 class VersionSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) description = models.CharField() class Meta: model = Version fields = '__all__' depth = 4 class OrgaSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) country = CountrySerializer(required=False, many=True) class Meta: model = Organization fields = '__all__' depth = 4 class Cost_factorSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) cost_factor_type = Cost_factor_typeSerializer(required=False, allow_null=True) amount = serializers.IntegerField() symbol = serializers.CharField() comment = serializers.CharField(required=False) class Meta: model = Cost_factor fields = '__all__' depth = 4 class TermSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) version = VersionSerializer(required=False, many=True) cost_factor = Cost_factorSerializer(required=False, many=True) licence = LicenceSerializer(required=False, many=True) class Meta: model = Term fields = '__all__' depth = 4 class ConditionTypeSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) condition_issuer = serializers.CharField() class Meta: model = ConditionType fields = '__all__' depth = 4 +class ConditionSubTypeSerializer(WritableNestedModelSerializer, RQLMixin): + id = serializers.IntegerField(required=False) + label = serializers.CharField() + + class Meta: + model = ConditionSubType + fields = '__all__' + depth = 4 + + class ConditionSetSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) term = TermSerializer(many=True, read_only=False) condition_type = ConditionTypeSerializer(read_only=False) + subtype = ConditionSubTypeSerializer(read_only=False) organization = OrgaSerializer(many=True, read_only=False) journal = JournalSerializer(many=True, read_only=False) comment = serializers.CharField(read_only=False) source = serializers.URLField(read_only=False) class Meta: model = ConditionSet # pre filter for rql # fields = ['id','condition_type','term','journal','organization'] # add for informations purpose fields = '__all__' depth = 4 class JournalIdSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) # allow update via post request --> "oa_status": {2}, class Meta: model = Journal fields = ['id'] class ConditionSetLightSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) term = TermSerializer(many=True, read_only=False) condition_type = ConditionTypeSerializer(read_only=False) + subtype = ConditionSubTypeSerializer(read_only=False) organization = OrgaSerializer(many=True, read_only=False) # No journals in this one. journal = JournalIdSerializer(many=True, read_only=False) comment = serializers.CharField(read_only=False) source = serializers.URLField(read_only=False) class Meta: model = ConditionSet # pre filter for rql # fields = ['id','condition_type','term','journal','organization'] # add for informations purpose - fields = ['id', 'condition_type', 'term', 'organization', 'journal', 'comment', 'source'] + fields = ['id', 'condition_type', 'subtype', 'term', 'organization', 'journal', 'comment', 'source'] depth = 4 class JournalLightSerializer(WritableNestedModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) # allow update via post request --> "oa_status": {2}, oa_status = serializers.PrimaryKeyRelatedField(queryset=Oa.objects.all()) language = serializers.PrimaryKeyRelatedField(queryset=Language.objects.all(), many=True) publisher = serializers.PrimaryKeyRelatedField(queryset=Publisher.objects.all(), many=True) + starting_year = serializers.IntegerField(required=False) + end_year = serializers.IntegerField(required=False) class Meta: model = Journal - fields = ['id', 'name', 'oa_status', 'language', 'publisher'] + fields = ['id', 'name', 'oa_status', 'language', 'publisher', 'starting_year', 'end_year'] depth = 1 class OaSerializer(WritableNestedModelSerializer,RQLMixin): id = serializers.IntegerField(required=False) status = serializers.CharField(allow_null=True) description = serializers.CharField(allow_null=True) subscription = serializers.BooleanField(required=False) accepted_manuscript = serializers.BooleanField(required=False) apc = serializers.BooleanField(required=False) final_version = serializers.BooleanField(required=False) class Meta: model = Oa fields = '__all__' depth = 4 class OrganizationConditionSerializer(serializers.ModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) organization = OrgaSerializer(required=False) condition_set = ConditionSetSerializer(required=False) class Meta: model = OrganizationCondition fields = '__all__' depth = 4 class JournalConditionSerializer(serializers.ModelSerializer, RQLMixin): id = serializers.IntegerField(required=False) journal = JournalSerializer(required=False) condition_set = ConditionSetSerializer(required=False) class Meta: model = JournalCondition fields = '__all__' depth = 4