From 381c6de61d8d1795dac22aebf453d3700e8de88a Mon Sep 17 00:00:00 2001 From: "J.-S. Caux" <J.S.Caux@uva.nl> Date: Wed, 26 Jul 2017 09:38:47 +0200 Subject: [PATCH] Add funders app, include in Crossmark metadata --- SciPost_v1/settings/base.py | 1 + SciPost_v1/urls.py | 1 + funders/__init__.py | 0 funders/admin.py | 9 ++ funders/apps.py | 5 + funders/forms.py | 22 +++ funders/migrations/0001_initial.py | 36 +++++ funders/migrations/0002_auto_20170725_2011.py | 23 +++ funders/migrations/0003_auto_20170726_0606.py | 19 +++ funders/migrations/__init__.py | 0 funders/models.py | 41 ++++++ funders/templates/funders/funders.html | 133 ++++++++++++++++++ .../funders/query_crossref_for_funder.html | 48 +++++++ funders/tests.py | 3 + funders/urls.py | 18 +++ funders/views.py | 65 +++++++++ journals/admin.py | 1 + .../migrations/0036_auto_20170725_1729.py | 40 ++++++ .../migrations/0037_auto_20170725_1730.py | 20 +++ .../migrations/0038_auto_20170725_1738.py | 33 +++++ .../migrations/0039_publication_grants.py | 21 +++ journals/models.py | 2 +- .../templates/journals/manage_metadata.html | 73 +++++++--- journals/urls/general.py | 3 + journals/views.py | 49 ++++++- 25 files changed, 642 insertions(+), 24 deletions(-) create mode 100644 funders/__init__.py create mode 100644 funders/admin.py create mode 100644 funders/apps.py create mode 100644 funders/forms.py create mode 100644 funders/migrations/0001_initial.py create mode 100644 funders/migrations/0002_auto_20170725_2011.py create mode 100644 funders/migrations/0003_auto_20170726_0606.py create mode 100644 funders/migrations/__init__.py create mode 100644 funders/models.py create mode 100644 funders/templates/funders/funders.html create mode 100644 funders/templates/funders/query_crossref_for_funder.html create mode 100644 funders/tests.py create mode 100644 funders/urls.py create mode 100644 funders/views.py create mode 100644 journals/migrations/0036_auto_20170725_1729.py create mode 100644 journals/migrations/0037_auto_20170725_1730.py create mode 100644 journals/migrations/0038_auto_20170725_1738.py create mode 100644 journals/migrations/0039_publication_grants.py diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py index 861544adb..28b54d4d4 100644 --- a/SciPost_v1/settings/base.py +++ b/SciPost_v1/settings/base.py @@ -95,6 +95,7 @@ INSTALLED_APPS = ( 'virtualmeetings', 'production', 'partners', + 'funders', 'webpack_loader', ) diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py index adf26e3aa..cb5b11d4b 100644 --- a/SciPost_v1/urls.py +++ b/SciPost_v1/urls.py @@ -20,6 +20,7 @@ urlpatterns = [ url(r'^commentaries/', include('commentaries.urls', namespace="commentaries")), url(r'^commentary/', include('commentaries.urls', namespace="commentaries")), url(r'^comments/', include('comments.urls', namespace="comments")), + url(r'^funders/', include('funders.urls', namespace="funders")), url(r'^journals/', include('journals.urls.general', namespace="journals")), url(r'^mailing_list/', include('mailing_lists.urls', namespace="mailing_lists")), url(r'^submissions/', include('submissions.urls', namespace="submissions")), diff --git a/funders/__init__.py b/funders/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/funders/admin.py b/funders/admin.py new file mode 100644 index 000000000..d049c6c0f --- /dev/null +++ b/funders/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from .models import Funder, Grant + + +admin.site.register(Funder) + + +admin.site.register(Grant) diff --git a/funders/apps.py b/funders/apps.py new file mode 100644 index 000000000..413a2e31c --- /dev/null +++ b/funders/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FundersConfig(AppConfig): + name = 'funders' diff --git a/funders/forms.py b/funders/forms.py new file mode 100644 index 000000000..64676af44 --- /dev/null +++ b/funders/forms.py @@ -0,0 +1,22 @@ +from django import forms + +from .models import Funder, Grant + +class FunderRegistrySearchForm(forms.Form): + name = forms.CharField(max_length=128) + + +class FunderForm(forms.ModelForm): + class Meta: + model = Funder + fields = ['name', 'identifier',] + + +class GrantForm(forms.ModelForm): + class Meta: + model = Grant + fields = ['funder', 'number', 'recipient_name', 'recipient',] + + +class GrantSelectForm(forms.Form): + grant = forms.ModelChoiceField(queryset=Grant.objects.all()) diff --git a/funders/migrations/0001_initial.py b/funders/migrations/0001_initial.py new file mode 100644 index 000000000..bf7e7e4fb --- /dev/null +++ b/funders/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-25 15:40 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('scipost', '0059_auto_20170701_1356'), + ] + + operations = [ + migrations.CreateModel( + name='Funder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256)), + ('identifier', models.CharField(max_length=200, unique=True)), + ], + ), + migrations.CreateModel( + name='Grant', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.CharField(max_length=64)), + ('recipient_name', models.CharField(blank=True, max_length=64, null=True)), + ('funder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='funders.Funder')), + ('recipient', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor')), + ], + ), + ] diff --git a/funders/migrations/0002_auto_20170725_2011.py b/funders/migrations/0002_auto_20170725_2011.py new file mode 100644 index 000000000..c689cf120 --- /dev/null +++ b/funders/migrations/0002_auto_20170725_2011.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-25 18:11 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='funder', + options={'ordering': ['name']}, + ), + migrations.AlterModelOptions( + name='grant', + options={'ordering': ['funder', 'recipient', 'recipient_name', 'number']}, + ), + ] diff --git a/funders/migrations/0003_auto_20170726_0606.py b/funders/migrations/0003_auto_20170726_0606.py new file mode 100644 index 000000000..d7c8df068 --- /dev/null +++ b/funders/migrations/0003_auto_20170726_0606.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-26 04:06 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0002_auto_20170725_2011'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='grant', + unique_together=set([('funder', 'number')]), + ), + ] diff --git a/funders/migrations/__init__.py b/funders/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/funders/models.py b/funders/models.py new file mode 100644 index 000000000..3957bf92f --- /dev/null +++ b/funders/models.py @@ -0,0 +1,41 @@ +from django.db import models + + +class Funder(models.Model): + """ + Funding info metadata is linked to funders from Crossref's + Fundref registry. + """ + name = models.CharField(max_length=256) + identifier = models.CharField(max_length=200, unique=True) + + class Meta: + ordering = ['name'] + + def __str__(self): + return self.name + + +class Grant(models.Model): + """ + An instance of a grant, award or other funding. + In a Publication's metadata, all grants are listed + in the Crossmark part of the metadata. + """ + funder = models.ForeignKey(Funder, on_delete=models.CASCADE) + number = models.CharField(max_length=64) + recipient_name = models.CharField(max_length=64, blank=True, null=True) + recipient = models.ForeignKey('scipost.Contributor', blank=True, null=True, + on_delete=models.CASCADE) + + class Meta: + ordering = ['funder', 'recipient', 'recipient_name', 'number'] + unique_together = ('funder', 'number') + + def __str__(self): + grantstring = '%s, grant number %s' % (str(self.funder), self.number) + if self.recipient: + grantstring += ' (%s)' % str(self.recipient) + elif self.recipient_name: + grantstring += ' (%s)' % self.recipient_name + return grantstring diff --git a/funders/templates/funders/funders.html b/funders/templates/funders/funders.html new file mode 100644 index 000000000..7ae67d06b --- /dev/null +++ b/funders/templates/funders/funders.html @@ -0,0 +1,133 @@ +{% extends 'scipost/base.html' %} + +{% block pagetitle %}: Funders{% endblock pagetitle %} + +{% load bootstrap %} + +{% block content %} + +<div class="row"> + <div class="col-12"> + <h1 class="highlight">Funders (and associated grants)</h1> + </div> +</div> + + +<div class="row"> + <div class="col-12"> + <div class="tab-nav-container"> + <div class="tab-nav-inner"> + <!-- Nav tabs --> + <ul class="nav btn-group personal-page-nav" role="tablist"> + <li class="nav-item btn btn-secondary"> + <a href="#funders" class="nav-link active" data-toggle="tab">Funders</a> + </li> + <li class="nav-item btn btn-secondary"> + <a href="#grants" class="nav-link" data-toggle="tab">Grants</a> + </li> + </ul> + </div> + </div> + </div> +</div> + + + +<div class="tab-content"> + + <!-- Tab: Funders --> + <div class="tab-pane active" id="funders" role="tabpanel"> + <div class="row"> + <div class="col-12"> + <h2 class="highlight">Funders</h2> + </div> + </div> + + <div class="row"> + <div class="col-12"> + <h2>Find a new funder in the Fundref registry</h2> + <form action="{% url 'funders:query_crossref_for_funder' %}" method="post"> + {% csrf_token %} + {{form|bootstrap}} + <input class="btn btn-secondary" type="submit" value="Search"> + </form> + <br/> + <h2>Funders in the SciPost database</h2> + <table class="table table-hover mb-5"> + <thead class="thead-default"> + <tr> + <th>Name</th> + <th>Identifier</th> + </tr> + </thead> + <tbody id="accordion" role="tablist" aria-multiselectable="true"> + {% for funder in funders %} + <tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ funder.id }}" aria-expanded="true" aria-controls="collapse{{ funder.id }}" style="cursor: pointer;"> + <td>{{ funder.name }}</td> + <td>{{ funder.identifier }}</td> + </tr> + {% empty %} + <tr> + <td colspan="3">No funders found</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + </div> + </div> + + + <!-- Tab: Grants --> + <div class="tab-pane" id="grants" role="tabpanel"> + <div class="row"> + <div class="col-12"> + <h2 class="highlight">Grants</h2> + </div> + </div> + + <div class="row"> + <div class="col-12"> + <h2>Add a grant</h2> + <form action="{% url 'funders:add_grant' %}" method="post"> + {% csrf_token %} + {{grant_form|bootstrap}} + <input class="btn btn-secondary" type="submit" value="Add"> + </form> + <br/> + <h2>Grants in the SciPost database</h2> + <table class="table table-hover mb-5"> + <thead class="thead-default"> + <tr> + <th>Funder Name</th> + <th>Recipient</th> + <th>Number</th> + </tr> + </thead> + <tbody id="accordion" role="tablist" aria-multiselectable="true"> + {% for grant in grants %} + <tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ grant.id }}" aria-expanded="true" aria-controls="collapse{{ grant.id }}" style="cursor: pointer;"> + <td>{{ grant.funder.name }}</td> + {% if grant.recipient %} + <td>{{ grant.recipient }}</td> + {% elif grant.recipient_name %} + <td>{{ grant.recipient_name }}</td> + {% else %} + <td></td> + {% endif %} + <td>{{ grant.number }}</td> + </tr> + {% empty %} + <tr> + <td colspan="3">No grants found</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + </div> + </div> +</div> + + +{% endblock content %} diff --git a/funders/templates/funders/query_crossref_for_funder.html b/funders/templates/funders/query_crossref_for_funder.html new file mode 100644 index 000000000..b8459c8a2 --- /dev/null +++ b/funders/templates/funders/query_crossref_for_funder.html @@ -0,0 +1,48 @@ +{% extends 'scipost/_personal_page_base.html' %} + +{% block pagetitle %}: Query Crossref for funder{% endblock pagetitle %} + +{% load bootstrap %} + +{% block content %} + +<div class="row"> + <div class="col-12"> + <h1 class="highlight">Query Crossref Fundref Registry for Funders</h1> + <form action="{% url 'funders:query_crossref_for_funder' %}" method="post"> + {% csrf_token %} + {{form|bootstrap}} + <input class="btn btn-secondary" type="submit" value="Search"> + </form> + {% if response_headers %} + <p>{{ response_headers }}</p> + {% endif %} + {% if response_text %} + <p>{{ response_text }}</p> + {% endif %} + {% if response %} + <p>{{ response }}</p> + <ul> + {% for item in response.message.items %} + <li> + {{ item.name }}, {{ item.id }}, {{ item.uri }} + <form action="{% url 'funders:add_funder' %}" method="post"> + {% csrf_token %} + <input name='name' style="width: 64%" value='{{ item.name }}'> + <input name='identifier' style="width: 64%" value='{{ item.uri }}'> + <input class="btn btn-secondary" type="submit" value="Add this funder"> + </form> + </li> + {% endfor %} + </ul> + <form action="{% url 'funders:add_funder' %}" method="post"> + {% csrf_token %} + {{funder_form|bootstrap}} + <input class="btn btn-secondary" type="submit" value="Submit"> + </form> + {% endif %} + </div> +</div> + + +{% endblock content %} diff --git a/funders/tests.py b/funders/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/funders/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/funders/urls.py b/funders/urls.py new file mode 100644 index 000000000..ec3521ede --- /dev/null +++ b/funders/urls.py @@ -0,0 +1,18 @@ +from django.conf.urls import url +from django.views.generic import TemplateView + +from . import views + +urlpatterns = [ + url(r'^$', views.funders, + name='funders'), + url(r'^query_crossref_for_funder$', + views.query_crossref_for_funder, + name='query_crossref_for_funder'), + url(r'^add_funder$', + views.add_funder, + name='add_funder'), + url(r'^add_grant$', + views.add_grant, + name='add_grant'), +] diff --git a/funders/views.py b/funders/views.py new file mode 100644 index 000000000..50f46f967 --- /dev/null +++ b/funders/views.py @@ -0,0 +1,65 @@ +import requests +import json + +from django.contrib import messages +from django.contrib.auth.decorators import permission_required +from django.core.urlresolvers import reverse +from django.shortcuts import render, redirect + +from .models import Funder, Grant +from .forms import FunderRegistrySearchForm, FunderForm, GrantForm + + +@permission_required('scipost.can_publish_accepted_submission', raise_exception=True) +def funders(request): + funders = Funder.objects.all() + form = FunderRegistrySearchForm() + grants = Grant.objects.all() + grant_form = GrantForm() + context = {'form': form, 'funders': funders, + 'grants': grants, 'grant_form': grant_form} + return render(request, 'funders/funders.html', context) + + +@permission_required('scipost.can_publish_accepted_submission', raise_exception=True) +def query_crossref_for_funder(request): + """ + Checks Crossref's Fundref Registry for an entry + corresponding to the funder name being looked for. + If found, creates a funders.Funder instance. + """ + form = FunderRegistrySearchForm(request.POST or None) + context = {'form': form} + if form.is_valid(): + queryurl = 'http://api.crossref.org/funders?query=%s' % form.cleaned_data['name'] + query = requests.get(queryurl) + response = json.loads(query.text) + context['response_headers'] = query.headers + context['response_text'] = query.text + context['response'] = response + context['funder_form'] = FunderForm() + return render(request, 'funders/query_crossref_for_funder.html', context) + + +@permission_required('scipost.can_publish_accepted_submission', raise_exception=True) +def add_funder(request): + form = FunderForm(request.POST or None) + if form.is_valid(): + funder = form.save() + messages.success(request, ('<h3>Funder %s successfully created</h3>') % + str(funder)) + elif form.has_changed(): + messages.warning(request, 'The form was invalidly filled.') + return redirect(reverse('funders:funders')) + + +@permission_required('scipost.can_publish_accepted_submission', raise_exception=True) +def add_grant(request): + grant_form = GrantForm(request.POST or None) + if grant_form.is_valid(): + grant = grant_form.save() + messages.success(request, ('<h3>Grant %s successfully added</h3>') % + str(grant)) + elif grant_form.has_changed(): + messages.warning(request, 'The form was invalidly filled (grant already exists?).') + return redirect(reverse('funders:funders')) diff --git a/journals/admin.py b/journals/admin.py index 6b5f4cecd..0ed07f17e 100644 --- a/journals/admin.py +++ b/journals/admin.py @@ -64,6 +64,7 @@ class PublicationAdmin(admin.ModelAdmin): admin.site.register(Publication, PublicationAdmin) + class DepositAdmin(admin.ModelAdmin): list_display = ('publication', 'timestamp', 'doi_batch_id', 'deposition_date',) readonly_fields = ('publication', 'doi_batch_id', 'metadata_xml', 'deposition_date',) diff --git a/journals/migrations/0036_auto_20170725_1729.py b/journals/migrations/0036_auto_20170725_1729.py new file mode 100644 index 000000000..65ec9846b --- /dev/null +++ b/journals/migrations/0036_auto_20170725_1729.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-25 15:29 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('scipost', '0059_auto_20170701_1356'), + ('journals', '0035_auto_20170714_0609'), + ] + + operations = [ + migrations.CreateModel( + name='Funder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256)), + ('identifier', models.CharField(max_length=200, unique=True)), + ], + ), + migrations.CreateModel( + name='Grant', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.CharField(max_length=64)), + ('recipient_name', models.CharField(blank=True, max_length=64, null=True)), + ('funder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journals.Funder')), + ('recipient', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor')), + ], + ), + migrations.AddField( + model_name='publication', + name='grants', + field=models.ManyToManyField(blank=True, null=True, to='journals.Grant'), + ), + ] diff --git a/journals/migrations/0037_auto_20170725_1730.py b/journals/migrations/0037_auto_20170725_1730.py new file mode 100644 index 000000000..db456745e --- /dev/null +++ b/journals/migrations/0037_auto_20170725_1730.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-25 15:30 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0036_auto_20170725_1729'), + ] + + operations = [ + migrations.AlterField( + model_name='publication', + name='grants', + field=models.ManyToManyField(blank=True, to='journals.Grant'), + ), + ] diff --git a/journals/migrations/0038_auto_20170725_1738.py b/journals/migrations/0038_auto_20170725_1738.py new file mode 100644 index 000000000..0d32403d1 --- /dev/null +++ b/journals/migrations/0038_auto_20170725_1738.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-25 15:38 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('journals', '0037_auto_20170725_1730'), + ] + + operations = [ + migrations.RemoveField( + model_name='grant', + name='funder', + ), + migrations.RemoveField( + model_name='grant', + name='recipient', + ), + migrations.RemoveField( + model_name='publication', + name='grants', + ), + migrations.DeleteModel( + name='Funder', + ), + migrations.DeleteModel( + name='Grant', + ), + ] diff --git a/journals/migrations/0039_publication_grants.py b/journals/migrations/0039_publication_grants.py new file mode 100644 index 000000000..8b2f9b1c3 --- /dev/null +++ b/journals/migrations/0039_publication_grants.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-25 15:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0001_initial'), + ('journals', '0038_auto_20170725_1738'), + ] + + operations = [ + migrations.AddField( + model_name='publication', + name='grants', + field=models.ManyToManyField(blank=True, to='funders.Grant'), + ), + ] diff --git a/journals/models.py b/journals/models.py index 722fdccdf..fb741cbbc 100644 --- a/journals/models.py +++ b/journals/models.py @@ -142,6 +142,7 @@ class Publication(models.Model): abstract = models.TextField() pdf_file = models.FileField(upload_to='UPLOADS/PUBLICATIONS/%Y/%m/', max_length=200) cc_license = models.CharField(max_length=32, choices=CC_LICENSES, default=CCBY4) + grants = models.ManyToManyField('funders.Grant', blank=True) metadata = JSONField(default={}, blank=True, null=True) metadata_xml = models.TextField(blank=True, null=True) # for Crossref deposit latest_metadata_update = models.DateTimeField(blank=True, null=True) @@ -216,7 +217,6 @@ class Publication(models.Model): return template.render(context) - class Deposit(models.Model): """ Each time a Crossref deposit is made for a Publication, diff --git a/journals/templates/journals/manage_metadata.html b/journals/templates/journals/manage_metadata.html index b9ca627d8..89f1796af 100644 --- a/journals/templates/journals/manage_metadata.html +++ b/journals/templates/journals/manage_metadata.html @@ -56,11 +56,12 @@ event: "focusin" </tr> <tr id="collapse{{ publication.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ publication.id }}" style="background-color: #fff;"> <td colspan="5"> - <h3 class="ml-3">Actions</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"> + + <h2 class="ml-3">Actions</h2> + <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> {% for author in publication.authors.all %} @@ -69,8 +70,6 @@ event: "focusin" </li> {% endfor %} </ul> - </div> - <div class="col-md-5"> <p>unregistered authors:</p> <ul> {% for author_unreg in publication.authors_unregistered.all %} @@ -79,20 +78,50 @@ event: "focusin" </li> {% endfor %} </ul> - </div> - </div> - </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> - <li><a href="{% url 'journals:create_funding_info_metadata' publication.doi_label %}">Create/update funding info metadata</a></li> - - <li><a href="{% url 'journals:create_metadata_xml' publication.doi_label %}">(re)create metadata</a></li> - <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'test' %}">Test metadata deposit (via Crossref test server)</a></li> - <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'deposit' %}">Deposit the metadata to Crossref</a></li> - <li><a href="{% url 'journals:produce_metadata_DOAJ' doi_label=publication.doi_label %}">Produce DOAJ metadata</a></li> - <li><a href="{% url 'journals:metadata_DOAJ_deposit' doi_label=publication.doi_label %}">Deposit the metadata to DOAJ</a></li> - </ul> - <h3 class="ml-3">Crossref Deposits</h3> + </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> + <li><a href="{% url 'journals:create_funding_info_metadata' publication.doi_label %}">Create/update funding info metadata</a></li> + + <li><a href="{% url 'journals:create_metadata_xml' publication.doi_label %}">(re)create metadata</a></li> + <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'test' %}">Test metadata deposit (via Crossref test server)</a></li> + <li><a href="{% url 'journals:metadata_xml_deposit' publication.doi_label 'deposit' %}">Deposit the metadata to Crossref</a></li> + <li><a href="{% url 'journals:produce_metadata_DOAJ' doi_label=publication.doi_label %}">Produce DOAJ metadata</a></li> + <li><a href="{% url 'journals:metadata_DOAJ_deposit' doi_label=publication.doi_label %}">Deposit the metadata to DOAJ</a></li> + </ul> + </div> + + <div class="col-md-5"> + <h2>Funding info for this publication:</h2> + {% if publication.funding_info %} + <p>{{ publication.funding_info }}</p> + {% else %} + <p>No funding info was found</p> + {% endif %} + <h2>Grants associated to this publication:</h2> + <ul> + {% for grant in publication.grants.all %} + <li> {{ grant }}</li> + {% empty %} + <li>no associated grants found</li> + {% endfor %} + </ul> + <br/> + <h3>Associate a grant to this publication:</h3> + <form action="{% url 'journals:add_associated_grant' publication.doi_label %}" method="post"> + {% csrf_token %} + {{associate_grant_form|bootstrap}} + <input class="btn btn-secondary" type="submit" value="Add"> + </form> + <h3>Other grant-related actions:</h3> + <ul> + <li><a href="{% url 'funders:funders' %}" target="_blank">go to the Funders page to add a Funder and/or Grant instance</a></li> + </ul> + </div> + </div> + + + <h2 class="ml-3">Crossref Deposits</h2> <table class="ml-5"> <thead class="thead-default"> <th>Timestamp</th> @@ -123,7 +152,7 @@ event: "focusin" </tbody> </table> - <h3 class="ml-3">DOAJ Deposits</h3> + <h2 class="ml-3">DOAJ Deposits</h2> <table class="ml-5"> <thead class="thead-default"> <th>Timestamp</th> diff --git a/journals/urls/general.py b/journals/urls/general.py index c785ce47b..b1ba8d539 100644 --- a/journals/urls/general.py +++ b/journals/urls/general.py @@ -50,6 +50,9 @@ urlpatterns = [ 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'^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'), diff --git a/journals/views.py b/journals/views.py index bcfa7155d..f6c21da83 100644 --- a/journals/views.py +++ b/journals/views.py @@ -23,9 +23,12 @@ from .forms import FundingInfoForm, InitiatePublicationForm, ValidatePublication UnregisteredAuthorForm, CreateMetadataXMLForm, CitationListBibitemsForm from .utils import JournalUtils +from funders.models import Funder from submissions.models import Submission from scipost.models import Contributor +from funders.forms import GrantSelectForm + from guardian.decorators import permission_required @@ -274,8 +277,10 @@ def validate_publication(request): @permission_required('scipost.can_publish_accepted_submission', return_403=True) def manage_metadata(request): publications = Publication.objects.order_by('-publication_date', '-paper_nr') + associate_grant_form = GrantSelectForm() context = { - 'publications': publications + 'publications': publications, + 'associate_grant_form': associate_grant_form, } return render(request, 'journals/manage_metadata.html', context) @@ -425,6 +430,22 @@ def create_funding_info_metadata(request, doi_label): return render(request, 'journals/create_funding_info_metadata.html', context) +@permission_required('scipost.can_publish_accepted_submission', return_403=True) +@transaction.atomic +def add_associated_grant(request, doi_label): + """ + Called by an Editorial Administrator. + This associates a grant from the database to this publication. + """ + publication = get_object_or_404(Publication, doi_label=doi_label) + grant_select_form = GrantSelectForm(request.POST or None) + if grant_select_form.is_valid(): + publication.grants.add(grant_select_form.cleaned_data['grant']) + publication.save() + messages.success(request, 'Grant added to publication %s' % str(publication)) + return redirect(reverse('journals:manage_metadata')) + + @permission_required('scipost.can_publish_accepted_submission', return_403=True) @transaction.atomic def create_metadata_xml(request, doi_label): @@ -457,6 +478,7 @@ def create_metadata_xml(request, doi_label): '<?xml version="1.0" encoding="UTF-8"?>\n' '<doi_batch version="4.4.0" xmlns="http://www.crossref.org/schema/4.4.0" ' 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + 'xmlns:fr="http://www.crossref.org/fundref.xsd" ' 'xsi:schemaLocation="http://www.crossref.org/schema/4.4.0 ' 'http://www.crossref.org/shema/deposit/crossref4.4.0.xsd">\n' '<head>\n' @@ -547,6 +569,31 @@ def create_metadata_xml(request, doi_label): '<crossmark_domain><domain>scipost.org</domain></crossmark_domain>\n' '</crossmark_domains>\n' '<crossmark_domain_exclusive>false</crossmark_domain_exclusive>\n' + '<custom_metadata>\n' + ) + funders = Funder.objects.filter(grant__in=publication.grants.all()).distinct() + nr_funders = funders.count() + if nr_funders > 0: + initial['metadata_xml'] += '<fr:program name="fundref">\n' + for funder in funders: + if nr_funders > 1: + initial['metadata_xml'] += '<fr:assertion name="fundgroup">\n' + initial['metadata_xml'] += ( + '<fr:assertion name="funder_name">' + funder.name + '\n' + '<fr:assertion name="funder_identifier">' + + funder.identifier + '</fr:assertion>\n' + '</fr:assertion>\n') + for grant in publication.grants.all(): + if grant.funder == funder: + initial['metadata_xml'] += ( + '<fr:assertion name="award_number">' + + grant.number + '</fr:assertion>\n') + if nr_funders > 1: + initial['metadata_xml'] += '</fr:assertion>\n' + initial['metadata_xml'] += '</fr:program>\n' + + initial['metadata_xml'] += ( + '</custom_metadata>\n' '</crossmark>\n' '<archive_locations><archive name="CLOCKSS"></archive></archive_locations>\n' '<doi_data>\n' -- GitLab