From 979b16d8de85166bb61d7cb8478989ab9c2294dd Mon Sep 17 00:00:00 2001 From: Jorran de Wit <jorrandewit@outlook.com> Date: Mon, 12 Feb 2018 21:55:24 +0100 Subject: [PATCH] Publication authors ordered registered or not --- general.py | 110 -------------- journals/admin.py | 11 +- journals/forms.py | 10 +- .../migrations/0009_auto_20180212_1947.py | 25 ++++ .../migrations/0010_auto_20180212_1947.py | 25 ++++ .../migrations/0011_auto_20180212_1950.py | 40 +++++ .../migrations/0012_auto_20180212_1950.py | 44 ++++++ journals/models.py | 56 ++++++- journals/templates/journals/add_author.html | 26 ++-- .../templates/journals/manage_metadata.html | 15 +- .../journals/publication_detail.html | 54 +++---- journals/urls/general.py | 11 +- journals/views.py | 141 ++++++++---------- scipost/forms.py | 2 +- scipost/migrations/0004_auto_20180212_1932.py | 22 +++ scipost/models.py | 3 +- scipost/views.py | 43 +++--- 17 files changed, 341 insertions(+), 297 deletions(-) delete mode 100644 general.py create mode 100644 journals/migrations/0009_auto_20180212_1947.py create mode 100644 journals/migrations/0010_auto_20180212_1947.py create mode 100644 journals/migrations/0011_auto_20180212_1950.py create mode 100644 journals/migrations/0012_auto_20180212_1950.py create mode 100644 scipost/migrations/0004_auto_20180212_1932.py diff --git a/general.py b/general.py deleted file mode 100644 index 5570b6627..000000000 --- a/general.py +++ /dev/null @@ -1,110 +0,0 @@ -from django.conf.urls import url -from django.urls import reverse_lazy -from django.views.generic import TemplateView, RedirectView - -from journals import views as journals_views - -urlpatterns = [ - # Journals - url(r'^$', journals_views.journals, name='journals'), - url(r'scipost_physics', RedirectView.as_view(url=reverse_lazy('scipost:landing_page', - args=['SciPostPhys']))), - url(r'^journals_terms_and_conditions$', - TemplateView.as_view(template_name='journals/journals_terms_and_conditions.html'), - name='journals_terms_and_conditions'), - url(r'^crossmark_policy$', - TemplateView.as_view(template_name='journals/crossmark_policy.html'), - name='crossmark_policy'), - - # Editorial and Administrative Workflow - url(r'^initiate_publication$', - journals_views.initiate_publication, - name='initiate_publication'), - url(r'^validate_publication$', - journals_views.validate_publication, - name='validate_publication'), - url(r'^mark_first_author/(?P<publication_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', - journals_views.mark_first_author, - name='mark_first_author'), - url(r'^mark_first_author_unregistered/(?P<publication_id>[0-9]+)/(?P<unregistered_author_id>[0-9]+)$', - journals_views.mark_first_author_unregistered, - name='mark_first_author_unregistered'), - url(r'^add_author/(?P<publication_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', - journals_views.add_author, - name='add_author'), - url(r'^add_author/(?P<publication_id>[0-9]+)$', - journals_views.add_author, - name='add_author'), - url(r'^add_unregistered_author/(?P<publication_id>[0-9]+)/(?P<unregistered_author_id>[0-9]+)$', - journals_views.add_unregistered_author, - name='add_unregistered_author'), - url(r'^add_new_unreg_author/(?P<publication_id>[0-9]+)$', - journals_views.add_new_unreg_author, - name='add_new_unreg_author'), - url(r'^manage_metadata/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.manage_metadata, - name='manage_metadata'), - url(r'^manage_metadata/(?P<issue_doi_label>[a-zA-Z]+.[0-9]+.[0-9]+)$', - journals_views.manage_metadata, - name='manage_metadata'), - url(r'^manage_metadata/$', - journals_views.manage_metadata, - name='manage_metadata'), - url(r'^create_citation_list_metadata/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.create_citation_list_metadata, - name='create_citation_list_metadata'), - url(r'^create_funding_info_metadata/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.create_funding_info_metadata, - name='create_funding_info_metadata'), - url(r'^add_associated_grant/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.add_associated_grant, - name='add_associated_grant'), - url(r'^add_generic_funder/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.add_generic_funder, - name='add_generic_funder'), - url(r'^create_metadata_xml/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.create_metadata_xml, - name='create_metadata_xml'), - url(r'^metadata_xml_deposit/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})/(?P<option>[a-z]+)$', - journals_views.metadata_xml_deposit, - name='metadata_xml_deposit'), - url(r'^mark_deposit_success/(?P<deposit_id>[0-9]+)/(?P<success>[0-1])$', - journals_views.mark_deposit_success, - name='mark_deposit_success'), - url(r'^produce_metadata_DOAJ/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.produce_metadata_DOAJ, - name='produce_metadata_DOAJ'), - url(r'^metadata_DOAJ_deposit/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.metadata_DOAJ_deposit, - name='metadata_DOAJ_deposit'), - url(r'^mark_doaj_deposit_success/(?P<deposit_id>[0-9]+)/(?P<success>[0-1])$', - journals_views.mark_doaj_deposit_success, - name='mark_doaj_deposit_success'), - url(r'^harvest_citedby_list/$', - journals_views.harvest_citedby_list, - name='harvest_citedby_list'), - url(r'^harvest_citedby_links/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', - journals_views.harvest_citedby_links, - name='harvest_citedby_links'), - url(r'^sign_existing_report/(?P<report_id>[0-9]+)$', - journals_views.sign_existing_report, - name='sign_existing_report'), - url(r'^manage_report_metadata/$', - journals_views.manage_report_metadata, - name='manage_report_metadata'), - url(r'^manage_comment_metadata/$', - journals_views.manage_comment_metadata, - name='manage_comment_metadata'), - url(r'^mark_report_doi_needed/(?P<report_id>[0-9]+)/(?P<needed>[0-1])$', - journals_views.mark_report_doi_needed, - name='mark_report_doi_needed'), - url(r'^mark_comment_doi_needed/(?P<comment_id>[0-9]+)/(?P<needed>[0-1])$', - journals_views.mark_comment_doi_needed, - name='mark_comment_doi_needed'), - url(r'^generic_metadata_xml_deposit/(?P<type_of_object>[a-z]+)/(?P<object_id>[0-9]+)$', - journals_views.generic_metadata_xml_deposit, - name='generic_metadata_xml_deposit'), - url(r'^mark_generic_deposit_success/(?P<deposit_id>[0-9]+)/(?P<success>[0-1])$', - journals_views.mark_generic_deposit_success, - name='mark_generic_deposit_success'), -] diff --git a/journals/admin.py b/journals/admin.py index 5aff8e167..18468831a 100644 --- a/journals/admin.py +++ b/journals/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin, messages from django import forms from journals.models import UnregisteredAuthor, Journal, Volume, Issue, Publication, \ - Deposit, DOAJDeposit, GenericDOIDeposit, Reference + Deposit, DOAJDeposit, GenericDOIDeposit, Reference, PublicationAuthorsTable from scipost.models import Contributor from submissions.models import Submission @@ -59,16 +59,21 @@ class ReferenceInline(admin.TabularInline): model = Reference +class AuthorsInline(admin.TabularInline): + model = PublicationAuthorsTable + extra = 0 + + class PublicationAdmin(admin.ModelAdmin): search_fields = ['title', 'author_list'] list_display = ['title', 'author_list', 'in_issue', 'doi_string', 'publication_date'] date_hierarchy = 'publication_date' list_filter = ['in_issue'] - inlines = [ReferenceInline] + inlines = [AuthorsInline, ReferenceInline] form = PublicationAdminForm -admin.site.register(Publication, PublicationAdmin) +admin.site.register(Publication, PublicationAdmin) class DepositAdmin(admin.ModelAdmin): diff --git a/journals/forms.py b/journals/forms.py index 232e62179..44462b752 100644 --- a/journals/forms.py +++ b/journals/forms.py @@ -6,9 +6,8 @@ from django import forms from django.forms import BaseModelFormSet, modelformset_factory from django.utils import timezone -from .models import Issue, Publication, Reference +from .models import Issue, Publication, Reference, UnregisteredAuthor -from scipost.models import Contributor from scipost.services import DOICaller from submissions.models import Submission @@ -31,12 +30,9 @@ class ValidatePublicationForm(forms.ModelForm): class UnregisteredAuthorForm(forms.ModelForm): - first_name = forms.CharField() - last_name = forms.CharField() - class Meta: - model = Contributor - fields = () + model = UnregisteredAuthor + fields = ('first_name', 'last_name') class CitationListBibitemsForm(forms.Form): diff --git a/journals/migrations/0009_auto_20180212_1947.py b/journals/migrations/0009_auto_20180212_1947.py new file mode 100644 index 000000000..3b45ebe54 --- /dev/null +++ b/journals/migrations/0009_auto_20180212_1947.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-02-12 18:47 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0008_auto_20180203_1229'), + ] + + operations = [ + migrations.RenameField( + model_name='publication', + old_name='authors', + new_name='authors_old', + ), + migrations.RenameField( + model_name='publication', + old_name='authors_unregistered', + new_name='authors_unregistered_old', + ), + ] diff --git a/journals/migrations/0010_auto_20180212_1947.py b/journals/migrations/0010_auto_20180212_1947.py new file mode 100644 index 000000000..6b60b98e1 --- /dev/null +++ b/journals/migrations/0010_auto_20180212_1947.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-02-12 18:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0009_auto_20180212_1947'), + ] + + operations = [ + migrations.AlterField( + model_name='publication', + name='authors_old', + field=models.ManyToManyField(blank=True, related_name='publications_old', to='scipost.Contributor'), + ), + migrations.AlterField( + model_name='publication', + name='authors_unregistered_old', + field=models.ManyToManyField(blank=True, related_name='publications_old', to='journals.UnregisteredAuthor'), + ), + ] diff --git a/journals/migrations/0011_auto_20180212_1950.py b/journals/migrations/0011_auto_20180212_1950.py new file mode 100644 index 000000000..a6003ecac --- /dev/null +++ b/journals/migrations/0011_auto_20180212_1950.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-02-12 18:50 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0004_auto_20180212_1932'), + ('journals', '0010_auto_20180212_1947'), + ] + + operations = [ + migrations.CreateModel( + name='PublicationAuthorsTable', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveSmallIntegerField()), + ('contributor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='scipost.Contributor')), + ('publication', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='authors', to='journals.Publication')), + ('unregistered_author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='journals.UnregisteredAuthor')), + ], + options={ + 'ordering': ('order',), + }, + ), + migrations.AddField( + model_name='publication', + name='authors_registered', + field=models.ManyToManyField(blank=True, related_name='publications', through='journals.PublicationAuthorsTable', to='scipost.Contributor'), + ), + migrations.AddField( + model_name='publication', + name='authors_unregistered', + field=models.ManyToManyField(blank=True, related_name='publications', through='journals.PublicationAuthorsTable', to='journals.UnregisteredAuthor'), + ), + ] diff --git a/journals/migrations/0012_auto_20180212_1950.py b/journals/migrations/0012_auto_20180212_1950.py new file mode 100644 index 000000000..69128e76f --- /dev/null +++ b/journals/migrations/0012_auto_20180212_1950.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-02-12 18:50 +from __future__ import unicode_literals + +from django.db import migrations + + +def transfer_publication_authors_to_separate_table(apps, schema_editor): + Contributor = apps.get_model('scipost', 'Contributor') + Publication = apps.get_model('journals', 'Publication') + UnregisteredAuthor = apps.get_model('journals', 'UnregisteredAuthor') + PublicationAuthorsTable = apps.get_model('journals', 'PublicationAuthorsTable') + + for publication in Publication.objects.all(): + registered_authors = Contributor.objects.filter(publications_old__id=publication.id) + unregistered_authors = UnregisteredAuthor.objects.filter(publications_old__id=publication.id) + + count = 1 + for author in registered_authors: + PublicationAuthorsTable.objects.create( + publication=publication, + contributor=author, + order=count, + ) + count += 1 + + for author in unregistered_authors: + PublicationAuthorsTable.objects.create( + publication=publication, + unregistered_author=author, + order=count, + ) + count += 1 + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0011_auto_20180212_1950'), + ] + + operations = [ + migrations.RunPython(transfer_publication_authors_to_separate_table) + ] diff --git a/journals/models.py b/journals/models.py index c583a4c52..41cd2052c 100644 --- a/journals/models.py +++ b/journals/models.py @@ -30,6 +30,46 @@ class UnregisteredAuthor(models.Model): return self.last_name + ', ' + self.first_name +class PublicationAuthorsTable(models.Model): + publication = models.ForeignKey('journals.Publication', related_name='authors') + unregistered_author = models.ForeignKey('journals.UnregisteredAuthor', null=True, blank=True, + related_name='+') + contributor = models.ForeignKey('scipost.Contributor', null=True, blank=True, related_name='+') + order = models.PositiveSmallIntegerField() + + class Meta: + ordering = ('order',) + + def __str__(self): + if self.contributor: + return str(self.contributor) + elif self.unregistered_author: + return str(self.unregistered_author) + + def save(self, *args, **kwargs): + if not self.order: + self.order = self.publication.authors.count() + 1 + return super().save(*args, **kwargs) + + @property + def is_registered(self): + return self.contributor is not None + + @property + def first_name(self): + if self.contributor: + return self.contributor.user.first_name + if self.unregistered_author: + return self.unregistered_author.first_name + + @property + def last_name(self): + if self.contributor: + return self.contributor.user.last_name + if self.unregistered_author: + return self.unregistered_author.last_name + + class Journal(models.Model): name = models.CharField(max_length=100, choices=SCIPOST_JOURNALS, unique=True) doi_label = models.CharField(max_length=200, unique=True, db_index=True, @@ -240,9 +280,15 @@ class Publication(models.Model): models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS), blank=True, null=True) # Authors - authors = models.ManyToManyField('scipost.Contributor', blank=True, - related_name='publications') + authors_registered = models.ManyToManyField('scipost.Contributor', blank=True, + through='PublicationAuthorsTable', + through_fields=('publication', 'contributor'), + related_name='publications') authors_unregistered = models.ManyToManyField('journals.UnregisteredAuthor', blank=True, + through='PublicationAuthorsTable', + through_fields=( + 'publication', + 'unregistered_author'), related_name='publications') first_author = models.ForeignKey('scipost.Contributor', blank=True, null=True, on_delete=models.CASCADE, @@ -282,6 +328,12 @@ class Publication(models.Model): latest_metadata_update = models.DateTimeField(blank=True, null=True) latest_activity = models.DateTimeField(default=timezone.now) + # Deprecated fields. About to be removed after successful database migration on production. + authors_old = models.ManyToManyField('scipost.Contributor', blank=True, + related_name='publications_old') + authors_unregistered_old = models.ManyToManyField('journals.UnregisteredAuthor', blank=True, + related_name='publications_old') + objects = PublicationQuerySet.as_manager() def __str__(self): diff --git a/journals/templates/journals/add_author.html b/journals/templates/journals/add_author.html index ce194a4f4..b49a3a040 100644 --- a/journals/templates/journals/add_author.html +++ b/journals/templates/journals/add_author.html @@ -29,18 +29,16 @@ <div class="row"> <div class="col-12"> - <h3>Current list of authors as contributors:</h3> + <h3>Current list of authors</h3> <ul> {% for author in publication.authors.all %} - <li><a href="{% url 'scipost:contributor_info' author.id %}">{{ author.user.first_name }} {{ author.user.last_name }}</a></li> - {% empty %} - <li>No unregistered authors known.</li> - {% endfor %} - </ul> - <h3>Current list of additional authors (unregistered):</h3> - <ul> - {% for author in publication.authors_unregistered.all %} - <li>{{ author }}</li> + <li> + {% if author.is_registered %} + <a href="{{ author.contributor.get_absolute_url }}">{{ author.contributor }}</a> + {% else %} + {{ author.unregistered_author }} + {% endif %} + </li> {% empty %} <li>No unregistered authors known.</li> {% endfor %} @@ -57,7 +55,7 @@ {% if form.has_changed %} <br> - <h3>Identified as contributor:</h3> + <h3>Identified as Contributor:</h3> <ul> {% for contributor in contributors_found %} <li> @@ -70,17 +68,17 @@ </ul> <h3>You can otherwise add the author manually and link it to the publication</h3> - <form action="{% url 'journals:add_new_unreg_author' publication_id=publication.id %}" method="post"> + <form action="{% url 'journals:add_author' publication_id=publication.id %}" method="post"> {% csrf_token %} {{ form|bootstrap }} <input class="btn btn-primary" type="submit" value="Add"> </form> <br> {% endif %} - - <p>Return to the <a href="{{publication.get_absolute_url}}">publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a>.</p> </div> </div> +<p>Return to the <a href="{{publication.get_absolute_url}}">publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a>.</p> + {% endblock content %} diff --git a/journals/templates/journals/manage_metadata.html b/journals/templates/journals/manage_metadata.html index 56f960111..0b0547b47 100644 --- a/journals/templates/journals/manage_metadata.html +++ b/journals/templates/journals/manage_metadata.html @@ -74,20 +74,11 @@ event: "focusin" <div class="row"> <div class="col-md-5"> <ul> - <li>Mark the first author (currently: {% if publication.first_author %}{{ publication.first_author }} {% elif publication.first_author_unregistered %}{{ publication.first_author_unregistered }} (unregistered){% endif %}) - <p>registered authors:</p> - <ul> + <li>Mark the first author (currently: {% if publication.first_author %}{{ publication.first_author }}{% elif publication.first_author_unregistered %}{{ publication.first_author_unregistered }} (unregistered){% endif %}) + <ul class="list-unstyled pl-4"> {% for author in publication.authors.all %} <li> - <a href="{% url 'journals:mark_first_author' publication_id=publication.id contributor_id=author.id %}">{{ author }}</a> - </li> - {% endfor %} - </ul> - <p>unregistered authors:</p> - <ul> - {% for author_unreg in publication.authors_unregistered.all %} - <li> - <a href="{% url 'journals:mark_first_author_unregistered' publication_id=publication.id unregistered_author_id=author_unreg.id %}">{{ author_unreg }}</a> + {{ author.order }}. <a href="{% url 'journals:mark_first_author' publication_id=publication.id author_object_id=author.id %}">{{ author }}</a> </li> {% endfor %} </ul> diff --git a/journals/templates/journals/publication_detail.html b/journals/templates/journals/publication_detail.html index 56ed342a7..358a8bbca 100644 --- a/journals/templates/journals/publication_detail.html +++ b/journals/templates/journals/publication_detail.html @@ -20,10 +20,11 @@ <meta name="citation_title" content="{{ publication.title }}"/> {% for author in publication.authors.all %} - <meta name="citation_author" content="{{ author.user.last_name }}, {{ author.user.first_name }}"/> - {% endfor %} - {% for author in publication.authors_unregistered.all %} - <meta name="citation_author" content="{{ author.last_name }}, {{ author.first_name }}"/> + {% if author.contributor %} + <meta name="citation_author" content="{{ author.contributor.user.last_name }}, {{ author.contributor.user.first_name }}"/> + {% elif author.unregistered_author %} + <meta name="citation_author" content="{{ author.unregistered_author.last_name }}, {{ author.unregistered_author.first_name }}"/> + {% endif %} {% endfor %} <meta name="citation_doi" content="{{ publication.doi_string }}"/> <meta name="citation_publication_date" content="{{ publication.publication_date|date:'Y/m/d' }}"/> @@ -83,10 +84,11 @@ <h3>Authors</h3> <ul> {% for author in publication.authors.all %} - <li><a href="{{author.get_absolute_url}}">{{ author }}</a></li> - {% endfor %} - {% for author in publication.authors_unregistered.all %} - <li>{{ author }}</li> + {% if author.is_registered %} + <li><a href="{{ author.contributor.get_absolute_url }}">{{ author.contributor }}</a></li> + {% else %} + <li>{{ author.unregistered_author }}</li> + {% endif %} {% endfor %} </ul> @@ -96,7 +98,7 @@ {# This function is not available for public yet! #} <em>The following is not available for the public yet:</em> {% include 'partials/journals/references.html' with publication=publication %} - + {% if publication.funders_generic.exists %} <h3>Funder{{ publication.funders_generic.count|pluralize }} for this publication:</h3> <ul> @@ -118,7 +120,7 @@ </div> </div> - {% if request.user and request.user.contributor in publication.authors.all %} + {% if request.user and request.user.contributor in publication.registered_authors.all %} <h3>Author actions</h3> <ul> <li><a href="{% url 'commentaries:comment_on_publication' publication.doi_label %}">Place a comment on this publication</a></li> @@ -131,29 +133,15 @@ <div class="col-12"> <h3>Editorial Administration tools: </h3> <ul> - <li>Mark the first author (currently: {% if publication.first_author %}{{ publication.first_author }} {% elif publication.first_author_unregistered %}{{ publication.first_author_unregistered }} (unregistered){% endif %}) - <div class="row"> - <div class="col-md-5"> - <p>registered authors:</p> - <ul> - {% for author in publication.authors.all %} - <li> - <a href="{% url 'journals:mark_first_author' publication_id=publication.id contributor_id=author.id %}">{{ author }}</a> - </li> - {% endfor %} - </ul> - </div> - <div class="col-md-5"> - <p>unregistered authors:</p> - <ul> - {% for author_unreg in publication.authors_unregistered.all %} - <li> - <a href="{% url 'journals:mark_first_author_unregistered' publication_id=publication.id unregistered_author_id=author_unreg.id %}">{{ author_unreg }}</a> - </li> - {% endfor %} - </ul> - </div> - </div> + <li> + Mark the first author (currently: {% if publication.first_author %}{{ publication.first_author }}{% elif publication.first_author_unregistered %}{{ publication.first_author_unregistered }} (unregistered){% endif %}) + <ul class="list-unstyled pl-4"> + {% for author in publication.authors.all %} + <li> + {{ author.order }}. <a href="{% url 'journals:mark_first_author' publication_id=publication.id author_object_id=author.id %}">{{ author }}</a> + </li> + {% endfor %} + </ul> </li> <li><a href="{% url 'journals:add_author' publication.id %}">Add a missing author</a></li> <li><a href="{% url 'journals:create_citation_list_metadata' publication.doi_label %}">Create/update citation list metadata</a></li> diff --git a/journals/urls/general.py b/journals/urls/general.py index dd9cfea9e..c57c961fd 100644 --- a/journals/urls/general.py +++ b/journals/urls/general.py @@ -23,24 +23,15 @@ urlpatterns = [ url(r'^validate_publication$', journals_views.validate_publication, name='validate_publication'), - url(r'^mark_first_author/(?P<publication_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', + url(r'^mark_first_author/(?P<publication_id>[0-9]+)/(?P<author_object_id>[0-9]+)$', journals_views.mark_first_author, name='mark_first_author'), - url(r'^mark_first_author_unregistered/(?P<publication_id>[0-9]+)/(?P<unregistered_author_id>[0-9]+)$', - journals_views.mark_first_author_unregistered, - name='mark_first_author_unregistered'), url(r'^add_author/(?P<publication_id>[0-9]+)/(?P<contributor_id>[0-9]+)$', journals_views.add_author, name='add_author'), url(r'^add_author/(?P<publication_id>[0-9]+)$', journals_views.add_author, name='add_author'), - url(r'^add_unregistered_author/(?P<publication_id>[0-9]+)/(?P<unregistered_author_id>[0-9]+)$', - journals_views.add_unregistered_author, - name='add_unregistered_author'), - url(r'^add_new_unreg_author/(?P<publication_id>[0-9]+)$', - journals_views.add_new_unreg_author, - name='add_new_unreg_author'), url(r'^manage_metadata/(?P<doi_label>[a-zA-Z]+.[0-9]+.[0-9]+.[0-9]{3,})$', journals_views.manage_metadata, name='manage_metadata'), diff --git a/journals/views.py b/journals/views.py index 863ca75f9..2456765e8 100644 --- a/journals/views.py +++ b/journals/views.py @@ -21,8 +21,8 @@ from django.shortcuts import get_object_or_404, render, redirect from .exceptions import PaperNumberingError from .helpers import paper_nr_string, issue_doi_label_from_doi_label -from .models import Journal, Issue, Publication, UnregisteredAuthor, Deposit, DOAJDeposit,\ - GenericDOIDeposit +from .models import Journal, Issue, Publication, Deposit, DOAJDeposit,\ + GenericDOIDeposit, PublicationAuthorsTable from .forms import FundingInfoForm, InitiatePublicationForm, ValidatePublicationForm,\ UnregisteredAuthorForm, CreateMetadataXMLForm, CitationListBibitemsForm,\ ReferenceFormSet @@ -246,18 +246,22 @@ def validate_publication(request): if validate_publication_form.is_valid(): publication = validate_publication_form.save() - # Fill in remaining data + # Fill remaining data submission = publication.accepted_submission - publication.authors.add(*submission.authors.all()) - if publication.first_author: - publication.authors.add(publication.first_author) if publication.first_author_unregistered: - publication.authors_unregistered.add(publication.first_author_unregistered) + PublicationAuthorsTable.objects.create( + order=1, + publication=publication, + unregistered_author=publication.first_author_unregistered) + + for submission_author in submission.authors.all(): + PublicationAuthorsTable.objects.create( + publication=publication, contributor=submission_author) publication.authors_claims.add(*submission.authors_claims.all()) publication.authors_false_claims.add(*submission.authors_false_claims.all()) # Add Institutions to the publication - for author in publication.authors.all(): + for author in publication.authors_registered.all(): for current_affiliation in author.affiliations.active(): publication.institutions.add(current_affiliation.institution) @@ -334,25 +338,29 @@ def manage_metadata(request, issue_doi_label=None, doi_label=None): @permission_required('scipost.can_publish_accepted_submission', return_403=True) -@transaction.atomic -def mark_first_author(request, publication_id, contributor_id): +def mark_first_author(request, publication_id, author_object_id): publication = get_object_or_404(Publication, id=publication_id) - contributor = get_object_or_404(Contributor, id=contributor_id) - publication.first_author = contributor - publication.first_author_unregistered = None - publication.save() - return redirect(reverse('journals:manage_metadata', - kwargs={'doi_label': publication.doi_label})) - + author_object = get_object_or_404(publication.authors, id=author_object_id) -@permission_required('scipost.can_publish_accepted_submission', return_403=True) -@transaction.atomic -def mark_first_author_unregistered(request, publication_id, unregistered_author_id): - publication = get_object_or_404(Publication, id=publication_id) - unregistered_author = get_object_or_404(UnregisteredAuthor, id=unregistered_author_id) - publication.first_author = None - publication.first_author_unregistered = unregistered_author + # Save explicit relation + if author_object.is_registered: + publication.first_author = author_object.contributor + publication.first_author_unregistered = None + else: + publication.first_author = None + publication.first_author_unregistered = author_object.unregistered_author publication.save() + + # Redo ordering + author_object.order = 1 + author_object.save() + author_objects = publication.authors.exclude(id=author_object.id) + count = 2 + for author in author_objects: + author.order = count + author.save() + count += 1 + messages.success(request, 'Marked {} first author'.format(author_object)) return redirect(reverse('journals:manage_metadata', kwargs={'doi_label': publication.doi_label})) @@ -369,22 +377,29 @@ def add_author(request, publication_id, contributor_id=None, unregistered_author publication = get_object_or_404(Publication, id=publication_id) if contributor_id: contributor = get_object_or_404(Contributor, id=contributor_id) - publication.authors.add(contributor) + PublicationAuthorsTable.objects.create(contributor=contributor, publication=publication) publication.save() + messages.success(request, 'Added {} as an author.'.format(contributor)) return redirect(reverse('journals:manage_metadata', kwargs={'doi_label': publication.doi_label})) contributors_found = None - if request.POST: - form = UnregisteredAuthorForm(request.POST) - if form.is_valid(): - contributors_found = Contributor.objects.filter( - user__last_name__icontains=form.cleaned_data['last_name']) - else: - form = UnregisteredAuthorForm(request.GET or None) - if form.is_valid(): - contributors_found = Contributor.objects.filter( - user__last_name__icontains=form.cleaned_data['last_name']) + form = UnregisteredAuthorForm(request.POST or request.GET or None) + + if request.POST and form.is_valid(): + unregistered_author = form.save() + PublicationAuthorsTable.objects.create( + publication=publication, + unregistered_author=unregistered_author) + messages.success(request, 'Added {} as an unregistered author.'.format( + unregistered_author + )) + return redirect(reverse('journals:manage_metadata', + kwargs={'doi_label': publication.doi_label})) + elif form.is_valid(): + contributors_found = Contributor.objects.filter( + # user__first_name__icontains=form.cleaned_data['first_name'], + user__last_name__icontains=form.cleaned_data['last_name']) context = { 'publication': publication, 'contributors_found': contributors_found, @@ -393,31 +408,6 @@ def add_author(request, publication_id, contributor_id=None, unregistered_author return render(request, 'journals/add_author.html', context) -@permission_required('scipost.can_publish_accepted_submission', return_403=True) -@transaction.atomic -def add_unregistered_author(request, publication_id, unregistered_author_id): - publication = get_object_or_404(Publication, id=publication_id) - unregistered_author = get_object_or_404(UnregisteredAuthor, id=unregistered_author_id) - publication.authors_unregistered.add(unregistered_author) - publication.save() - return redirect(reverse('journals:manage_metadata', - kwargs={'doi_label': publication.doi_label})) - - -@permission_required('scipost.can_publish_accepted_submission', return_403=True) -@transaction.atomic -def add_new_unreg_author(request, publication_id): - publication = get_object_or_404(Publication, id=publication_id) - if request.method == 'POST': - new_unreg_author_form = UnregisteredAuthorForm(request.POST) - if new_unreg_author_form.is_valid(): - new_unreg_author = new_unreg_author_form.save() - publication.authors_unregistered.add(new_unreg_author) - return redirect(reverse('journals:manage_metadata', - kwargs={'doi_label': publication.doi_label})) - raise Http404 - - @permission_required('scipost.can_publish_accepted_submission', return_403=True) @transaction.atomic def create_citation_list_metadata(request, doi_label): @@ -604,40 +594,27 @@ def create_metadata_xml(request, doi_label): '</journal_issue>\n' '<journal_article publication_type=\'full_text\'>\n' '<titles><title>' + publication.title + '</title></titles>\n' - '<contributors>\n' ) # Precondition: all authors MUST be listed in authors field of publication instance, # this to be checked by EdAdmin before publishing. - for author in publication.authors.all(): - if author == publication.first_author: + initial['metadata_xml'] += '<contributors>\n' + for author_object in publication.authors.all(): + if author_object.order == 1: initial['metadata_xml'] += ( '<person_name sequence=\'first\' contributor_role=\'author\'> ' - '<given_name>' + author.user.first_name + '</given_name> ' - '<surname>' + author.user.last_name + '</surname> ' + '<given_name>' + author_object.first_name + '</given_name> ' + '<surname>' + author_object.last_name + '</surname> ' ) else: initial['metadata_xml'] += ( '<person_name sequence=\'additional\' contributor_role=\'author\'> ' - '<given_name>' + author.user.first_name + '</given_name> ' - '<surname>' + author.user.last_name + '</surname> ' - ) - if author.orcid_id: - initial['metadata_xml'] += '<ORCID>http://orcid.org/' + author.orcid_id + '</ORCID>' - initial['metadata_xml'] += '</person_name>\n' - - for author_unreg in publication.authors_unregistered.all(): - if author_unreg == publication.first_author_unregistered: - initial['metadata_xml'] += ( - '<person_name sequence=\'first\' contributor_role=\'author\'> ' - '<given_name>' + author_unreg.first_name + '</given_name> ' - '<surname>' + author_unreg.last_name + '</surname> ' + '<given_name>' + author_object.first_name + '</given_name> ' + '<surname>' + author_object.last_name + '</surname> ' ) - else: + if author_object.contributor and author_object.contributor.orcid_id: initial['metadata_xml'] += ( - '<person_name sequence=\'additional\' contributor_role=\'author\'> ' - '<given_name>' + author_unreg.first_name + '</given_name> ' - '<surname>' + author_unreg.last_name + '</surname> ' + '<ORCID>http://orcid.org/' + author_object.contributor.orcid_id + '</ORCID>' ) initial['metadata_xml'] += '</person_name>\n' initial['metadata_xml'] += '</contributors>\n' diff --git a/scipost/forms.py b/scipost/forms.py index 66d4f85c8..e52294ab5 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -313,7 +313,7 @@ class UpdatePersonalDataForm(forms.ModelForm): and changes the orcid_id. It marks all Publications, Reports and Comments authors by this Contributor with a deposit_requires_update == True. """ - publications = Publication.objects.filter(authors=self.instance) + publications = Publication.objects.filter(authors_registered=self.instance) for publication in publications: publication.doideposit_needs_updating = True publication.save() diff --git a/scipost/migrations/0004_auto_20180212_1932.py b/scipost/migrations/0004_auto_20180212_1932.py new file mode 100644 index 000000000..f7e69e16c --- /dev/null +++ b/scipost/migrations/0004_auto_20180212_1932.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-02-12 18:32 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0003_auto_20180206_1940'), + ] + + operations = [ + migrations.AlterField( + model_name='contributor', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/scipost/models.py b/scipost/models.py index 7e964c1db..b217d78e7 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -39,8 +39,7 @@ class Contributor(models.Model): All *science* users of SciPost are Contributors. username, password, email, first_name and last_name are inherited from User. """ - user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, - unique=True, blank=True, null=True) + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, unique=True) invitation_key = models.CharField(max_length=40, blank=True) activation_key = models.CharField(max_length=40, blank=True) key_expires = models.DateTimeField(default=timezone.now) diff --git a/scipost/views.py b/scipost/views.py index 64fd94a57..a5623844c 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -44,7 +44,7 @@ from affiliations.forms import AffiliationsFormset from colleges.permissions import fellowship_or_admin_required from commentaries.models import Commentary from comments.models import Comment -from journals.models import Publication, Journal +from journals.models import Publication, Journal, PublicationAuthorsTable from mails.views import MailEditingSubView from news.models import NewsItem from submissions.models import Submission, RefereeInvitation,\ @@ -798,7 +798,7 @@ def _personal_page_publications(request): } context['nr_publication_authorships_to_claim'] = Publication.objects.filter( author_list__contains=request.user.last_name).exclude( - authors=contributor).exclude( + authors_registered=contributor).exclude( authors_claims=contributor).exclude( authors_false_claims=contributor).count() return render(request, 'partials/scipost/personal_page/publications.html', context) @@ -1026,28 +1026,28 @@ def claim_authorships(request): contributor = Contributor.objects.get(user=request.user) publication_authorships_to_claim = (Publication.objects - .filter(author_list__contains=contributor.user.last_name) - .exclude(authors__in=[contributor]) - .exclude(authors_claims__in=[contributor]) - .exclude(authors_false_claims__in=[contributor])) + .filter(author_list__contains=contributor.user.last_name) + .exclude(authors_registered=contributor) + .exclude(authors_claims=contributor) + .exclude(authors_false_claims=contributor)) pub_auth_claim_form = AuthorshipClaimForm() submission_authorships_to_claim = (Submission.objects .filter(author_list__contains=contributor.user.last_name) - .exclude(authors__in=[contributor]) - .exclude(authors_claims__in=[contributor]) - .exclude(authors_false_claims__in=[contributor])) + .exclude(authors=contributor) + .exclude(authors_claims=contributor) + .exclude(authors_false_claims=contributor)) sub_auth_claim_form = AuthorshipClaimForm() commentary_authorships_to_claim = (Commentary.objects .filter(author_list__contains=contributor.user.last_name) - .exclude(authors__in=[contributor]) - .exclude(authors_claims__in=[contributor]) - .exclude(authors_false_claims__in=[contributor])) + .exclude(authors=contributor) + .exclude(authors_claims=contributor) + .exclude(authors_false_claims=contributor)) com_auth_claim_form = AuthorshipClaimForm() thesis_authorships_to_claim = (ThesisLink.objects .filter(author__contains=contributor.user.last_name) - .exclude(author_as_cont__in=[contributor]) - .exclude(author_claims__in=[contributor]) - .exclude(author_false_claims__in=[contributor])) + .exclude(author_as_cont=contributor) + .exclude(author_claims=contributor) + .exclude(author_false_claims=contributor)) thesis_auth_claim_form = AuthorshipClaimForm() context = { @@ -1140,16 +1140,17 @@ def vet_authorship_claim(request, claim_id, claim): vetting_contributor = Contributor.objects.get(user=request.user) claim_to_vet = AuthorshipClaim.objects.get(pk=claim_id) - if claim_to_vet.publication is not None: + if claim_to_vet.publication: claim_to_vet.publication.authors_claims.remove(claim_to_vet.claimant) if claim == '1': - claim_to_vet.publication.authors.add(claim_to_vet.claimant) + PublicationAuthorsTable.objects.create( + publication=claim_to_vet.publication, contributor=claim_to_vet.claimant) claim_to_vet.status = '1' elif claim == '0': claim_to_vet.publication.authors_false_claims.add(claim_to_vet.claimant) claim_to_vet.status = '-1' claim_to_vet.publication.save() - if claim_to_vet.submission is not None: + if claim_to_vet.submission: claim_to_vet.submission.authors_claims.remove(claim_to_vet.claimant) if claim == '1': claim_to_vet.submission.authors.add(claim_to_vet.claimant) @@ -1158,7 +1159,7 @@ def vet_authorship_claim(request, claim_id, claim): claim_to_vet.submission.authors_false_claims.add(claim_to_vet.claimant) claim_to_vet.status = '-1' claim_to_vet.submission.save() - if claim_to_vet.commentary is not None: + if claim_to_vet.commentary: claim_to_vet.commentary.authors_claims.remove(claim_to_vet.claimant) if claim == '1': claim_to_vet.commentary.authors.add(claim_to_vet.claimant) @@ -1167,7 +1168,7 @@ def vet_authorship_claim(request, claim_id, claim): claim_to_vet.commentary.authors_false_claims.add(claim_to_vet.claimant) claim_to_vet.status = '-1' claim_to_vet.commentary.save() - if claim_to_vet.thesislink is not None: + if claim_to_vet.thesislink: claim_to_vet.thesislink.author_claims.remove(claim_to_vet.claimant) if claim == '1': claim_to_vet.thesislink.author_as_cont.add(claim_to_vet.claimant) @@ -1189,7 +1190,7 @@ def contributor_info(request, contributor_id): on the relevant name (in listing headers of Submissions, ...). """ contributor = get_object_or_404(Contributor, pk=contributor_id) - contributor_publications = Publication.objects.published().filter(authors=contributor) + contributor_publications = Publication.objects.published().filter(authors_registered=contributor) contributor_submissions = Submission.objects.public_unlisted().filter(authors=contributor) contributor_commentaries = Commentary.objects.filter(authors=contributor) contributor_theses = ThesisLink.objects.vetted().filter(author_as_cont=contributor) -- GitLab