diff --git a/scipost_django/journals/constants.py b/scipost_django/journals/constants.py index e485dbf61b18b8d828f8e39bf269b60a619e21ad..8b0a66a28b9b480d9d9b29bf8c2cc63998240790 100644 --- a/scipost_django/journals/constants.py +++ b/scipost_django/journals/constants.py @@ -34,6 +34,35 @@ CC_LICENSES_URI = ( ) +PUBLISHABLE_OBJECT_TYPE_ARTICLE = "article" +PUBLISHABLE_OBJECT_TYPE_CODEBASE = "codebase" +PUBLISHABLE_OBJECT_TYPE_DATASET = "dataset" +PUBLISHABLE_OBJECT_TYPE_CHOICES = ( + (PUBLISHABLE_OBJECT_TYPE_ARTICLE, "Article"), + (PUBLISHABLE_OBJECT_TYPE_CODEBASE, "Codebase release"), + (PUBLISHABLE_OBJECT_TYPE_DATASET, "Dataset"), +) + +def get_publishable_object_types_default_list(): + return [PUBLISHABLE_OBJECT_TYPE_ARTICLE,] + +def get_submission_object_types_default(): + return { + "options": [ + ' + '.join(l) for l in [ + [PUBLISHABLE_OBJECT_TYPE_ARTICLE,], + [PUBLISHABLE_OBJECT_TYPE_ARTICLE, PUBLISHABLE_OBJECT_TYPE_CODEBASE], + [PUBLISHABLE_OBJECT_TYPE_ARTICLE, PUBLISHABLE_OBJECT_TYPE_DATASET], + [ + PUBLISHABLE_OBJECT_TYPE_ARTICLE, + PUBLISHABLE_OBJECT_TYPE_CODEBASE, + PUBLISHABLE_OBJECT_TYPE_DATASET, + ], + ] + ] + } + + ISSUES_AND_VOLUMES = "IV" ISSUES_ONLY = "IO" INDIVIDUAL_PUBLICATIONS = "IP" diff --git a/scipost_django/journals/migrations/0125_journal_publishable_object_types.py b/scipost_django/journals/migrations/0125_journal_publishable_object_types.py new file mode 100644 index 0000000000000000000000000000000000000000..bee75146a05004eddb0982f59d426c54ca9b1fd5 --- /dev/null +++ b/scipost_django/journals/migrations/0125_journal_publishable_object_types.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.16 on 2023-01-21 08:29 + +from django.db import migrations, models +import journals.constants +import scipost.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0124_journal_has_clockss'), + ] + + operations = [ + migrations.AddField( + model_name='journal', + name='publishable_object_types', + field=scipost.fields.ChoiceArrayField(base_field=models.CharField(choices=[('article', 'Article'), ('codebase', 'Codebase release'), ('dataset', 'Dataset')], max_length=24), default=journals.constants.get_publishable_object_types_default_list, size=None), + ), + ] diff --git a/scipost_django/journals/migrations/0126_populate_journal_publishable_object_types.py b/scipost_django/journals/migrations/0126_populate_journal_publishable_object_types.py new file mode 100644 index 0000000000000000000000000000000000000000..f6183966ea61e46f111e4fe94094aed813363186 --- /dev/null +++ b/scipost_django/journals/migrations/0126_populate_journal_publishable_object_types.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.16 on 2023-01-21 08:33 + +from django.db import migrations + +from journals.constants import PUBLISHABLE_OBJECT_TYPE_CODEBASE + +def populate_codebases(apps, schema_editor): + Journal = apps.get_model("journals.Journal") + + for j in Journal.objects.all(): + if "Codebases" in j.name: + j.publishable_object_types.append(PUBLISHABLE_OBJECT_TYPE_CODEBASE) + j.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0125_journal_publishable_object_types'), + ] + + operations = [ + migrations.RunPython( + populate_codebases, + reverse_code=migrations.RunPython.noop, + ) + ] diff --git a/scipost_django/journals/migrations/0127_journal_submission_object_types.py b/scipost_django/journals/migrations/0127_journal_submission_object_types.py new file mode 100644 index 0000000000000000000000000000000000000000..7baeab5721a1870891328750f35e7a8a04c87441 --- /dev/null +++ b/scipost_django/journals/migrations/0127_journal_submission_object_types.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.16 on 2023-01-21 13:29 + +from django.db import migrations, models +import journals.constants + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0126_populate_journal_publishable_object_types'), + ] + + operations = [ + migrations.AddField( + model_name='journal', + name='submission_object_types', + field=models.JSONField(default=journals.constants.get_submission_object_types_default), + ), + ] diff --git a/scipost_django/journals/migrations/0128_populate_submission_object_types.py b/scipost_django/journals/migrations/0128_populate_submission_object_types.py new file mode 100644 index 0000000000000000000000000000000000000000..27658db48d6f0a2856d8e48e4df3ca657091b692 --- /dev/null +++ b/scipost_django/journals/migrations/0128_populate_submission_object_types.py @@ -0,0 +1,52 @@ +# Generated by Django 3.2.16 on 2023-01-21 13:30 + +from django.db import migrations + + +from journals.constants import ( + PUBLISHABLE_OBJECT_TYPE_ARTICLE, + PUBLISHABLE_OBJECT_TYPE_CODEBASE, + PUBLISHABLE_OBJECT_TYPE_DATASET, +) + + +def populate_codebases(apps, schema_editor): + Journal = apps.get_model("journals.Journal") + + for j in Journal.objects.all(): + if "Codebases" in j.name: + j.submission_object_types = { + "options": [ + ' + '.join(l) for l in [ + [PUBLISHABLE_OBJECT_TYPE_CODEBASE,], + [ + PUBLISHABLE_OBJECT_TYPE_ARTICLE, + PUBLISHABLE_OBJECT_TYPE_CODEBASE, + ], + [ + PUBLISHABLE_OBJECT_TYPE_CODEBASE, + PUBLISHABLE_OBJECT_TYPE_DATASET, + ], + [ + PUBLISHABLE_OBJECT_TYPE_ARTICLE, + PUBLISHABLE_OBJECT_TYPE_CODEBASE, + PUBLISHABLE_OBJECT_TYPE_DATASET, + ], + ] + ] + } + j.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0127_journal_submission_object_types'), + ] + + operations = [ + migrations.RunPython( + populate_codebases, + reverse_code=migrations.RunPython.noop, + ) + ] diff --git a/scipost_django/journals/models/journal.py b/scipost_django/journals/models/journal.py index b7c17a861965194d804d22526fff3749fe264a5a..0b3d519a12da317a86c03707dc427008854c6b73 100644 --- a/scipost_django/journals/models/journal.py +++ b/scipost_django/journals/models/journal.py @@ -9,9 +9,17 @@ from django.db.models import Avg, F from django.urls import reverse from django.utils import timezone +from scipost.fields import ChoiceArrayField from series.models import Collection -from ..constants import JOURNAL_STRUCTURE, ISSUES_AND_VOLUMES, ISSUES_ONLY +from ..constants import ( + PUBLISHABLE_OBJECT_TYPE_CHOICES, + get_publishable_object_types_default_list, + get_submission_object_types_default, + JOURNAL_STRUCTURE, + ISSUES_AND_VOLUMES, + ISSUES_ONLY, +) from ..managers import JournalQuerySet from ..validators import doi_journal_validator @@ -50,15 +58,28 @@ class Journal(models.Model): default="SciPost [abbrev]", help_text="Abbreviated name (for use in citations)", ) + doi_label = models.CharField( max_length=200, unique=True, db_index=True, validators=[doi_journal_validator] ) issn = models.CharField(max_length=16, default="2542-4653", blank=True) + active = models.BooleanField(default=True) + submission_allowed = models.BooleanField(default=True) + + publishable_object_types = ChoiceArrayField( + models.CharField(max_length=24, choices=PUBLISHABLE_OBJECT_TYPE_CHOICES), + default=get_publishable_object_types_default_list, + ) + submission_object_types = models.JSONField( + default=get_submission_object_types_default, + ) + structure = models.CharField( max_length=2, choices=JOURNAL_STRUCTURE, default=ISSUES_AND_VOLUMES ) + refereeing_period = models.DurationField(default=datetime.timedelta(days=28)) style = models.TextField( @@ -78,6 +99,7 @@ class Journal(models.Model): list_order = models.PositiveSmallIntegerField(default=100) # For manuscript preparation: templates are given by the SubmissionTemplate related objects + # For the author guidelines page: required_article_elements = models.TextField( default="[To be filled in; you can use markup]" @@ -92,6 +114,7 @@ class Journal(models.Model): submission_insert = models.TextField( blank=True, null=True, default="[Optional; you can use markup]" ) + minimal_nr_of_reports = models.PositiveSmallIntegerField( help_text=( "Minimal number of substantial Reports required " diff --git a/scipost_django/profiles/models.py b/scipost_django/profiles/models.py index faa723e2e26d0433141fc292beb2e7762321452a..8646d93e08fe843920e70b9e0b905fd7a978e811 100644 --- a/scipost_django/profiles/models.py +++ b/scipost_django/profiles/models.py @@ -9,7 +9,6 @@ from django.shortcuts import get_object_or_404 from scipost.behaviors import orcid_validator from scipost.constants import TITLE_CHOICES, TITLE_DR -from scipost.fields import ChoiceArrayField from scipost.models import Contributor from comments.models import Comment diff --git a/scipost_django/submissions/forms/__init__.py b/scipost_django/submissions/forms/__init__.py index 0561ddc5b3f60f068f137a101743a26112fc9d35..8d26eb1440a2c0b8a6aee43ce955a07a88e55629 100644 --- a/scipost_django/submissions/forms/__init__.py +++ b/scipost_django/submissions/forms/__init__.py @@ -65,6 +65,11 @@ from ..regexes import CHEMRXIV_DOI_PATTERN from colleges.models import Fellowship from common.utils import Q_with_alternative_spellings from journals.models import Journal +from journals.constants import ( + PUBLISHABLE_OBJECT_TYPE_ARTICLE, + PUBLISHABLE_OBJECT_TYPE_CODEBASE, + PUBLISHABLE_OBJECT_TYPE_DATASET, +) from mails.utils import DirectMailUtil from ontology.models import AcademicField, Specialty from preprints.helpers import get_new_scipost_identifier @@ -1141,7 +1146,8 @@ class SubmissionForm(forms.ModelForm): help_text=( "Please submit the processed .pdf (not the source files; " "these will only be required at the post-acceptance proofs stage)" - ) + ), + required=False, ) class Meta: @@ -1282,13 +1288,7 @@ class SubmissionForm(forms.ModelForm): self.requested_by, cleaned_data["is_resubmission_of"] ) - if "Codeb" in cleaned_data["submitted_to"].doi_label: - if not ("code_repository_url" in cleaned_data and cleaned_data["code_repository_url"]): - msg = ( - "You must specify a code repository if you submit to %s" - % cleaned_data["submitted_to"].name - ) - self.add_error("code_repository_url", msg) + self.clear_submission_object_types() if "Proc" not in cleaned_data["submitted_to"].doi_label: try: @@ -1298,6 +1298,31 @@ class SubmissionForm(forms.ModelForm): pass return cleaned_data + def clear_submission_object_types(self): + """ + Check that the submitted material fits one of the Journal's options. + """ + submitted_types = [] + if (self.cleaned_data.get("preprint_file", None) or + self.cleaned_data.get("preprint_link", None)): + submitted_types.append(PUBLISHABLE_OBJECT_TYPE_ARTICLE) + if self.cleaned_data.get("code_repository_url", None): + submitted_types.append(PUBLISHABLE_OBJECT_TYPE_CODEBASE) + if self.cleaned_data.get("data_repository_url", None): + submitted_types.append(PUBLISHABLE_OBJECT_TYPE_DATASET) + submitted_types.sort() # not needed here, but for future safety + submitted_types_code = ' + '.join(submitted_types) + options = self.cleaned_data["submitted_to"].submission_object_types["options"] + if submitted_types_code not in options: + self.add_error( + None, + ( + f"You are trying to submit document types: {submitted_types_code}, " + "but this Journal requires one of the following options: " + f"{', '.join(options)}" + ) + ) + def clean_author_list(self): """ Check if author list matches the Contributor submitting. diff --git a/scipost_django/submissions/models/submission.py b/scipost_django/submissions/models/submission.py index b93705ef96ac5ca1aa901ac87d340a8d931f1d2a..5c3ac7c30aa0b2a1ba8cedd0aa038f4f36df7fe2 100644 --- a/scipost_django/submissions/models/submission.py +++ b/scipost_django/submissions/models/submission.py @@ -283,6 +283,7 @@ class Submission(models.Model): "to set things up." ), ) + title = models.CharField(max_length=300) # Authors which have been mapped to contributors: diff --git a/scipost_django/submissions/views/pool.py b/scipost_django/submissions/views/pool.py index 4b001a7871119dd4962ea529e8bfd723122d2ed6..eeb35f45707feecc48f0512a04082fb705739718 100644 --- a/scipost_django/submissions/views/pool.py +++ b/scipost_django/submissions/views/pool.py @@ -118,7 +118,6 @@ def add_remark(request, identifier_w_vn_nr): remark = Remark( contributor=request.user.contributor, submission=submission, - date=timezone.now(), remark=remark_form.cleaned_data["remark"], ) remark.save()