diff --git a/affiliations/managers.py b/affiliations/managers.py index e6a0872050e865cd4a2e7fdf3efcab9fae156f2e..11d16f9ecedba5af4c15825c04fd3a594c28193b 100644 --- a/affiliations/managers.py +++ b/affiliations/managers.py @@ -16,3 +16,7 @@ class AffiliationQuerySet(models.QuerySet): Q(begin_date__isnull=True, end_date__gte=today) | Q(begin_date__lte=today, end_date__gte=today) | Q(begin_date__isnull=True, end_date__isnull=True)) + +class InstitutionQuerySet(models.QuerySet): + def has_publications(self): + return self.filter(publications__isnull=False) diff --git a/affiliations/models.py b/affiliations/models.py index 40f41195d423482d132f86ceb33536c7fb26e7ea..faf52284684048e97d7729cce9e03c55e87862d8 100644 --- a/affiliations/models.py +++ b/affiliations/models.py @@ -10,18 +10,19 @@ from django_countries.fields import CountryField from scipost.models import Contributor from .constants import INSTITUTION_TYPES, TYPE_UNIVERSITY -from .managers import AffiliationQuerySet +from .managers import AffiliationQuerySet, InstitutionQuerySet class Institution(models.Model): - """ - Any (scientific) Institution in the world should ideally have a SciPost registration. - """ + """Any (scientific) Institution with a SciPost registration.""" + name = models.CharField(max_length=255) acronym = models.CharField(max_length=16, blank=True) country = CountryField() type = models.CharField(max_length=16, choices=INSTITUTION_TYPES, default=TYPE_UNIVERSITY) + objects = InstitutionQuerySet.as_manager() + class Meta: default_related_name = 'institutions' ordering = ['country'] @@ -30,9 +31,11 @@ class Institution(models.Model): return '{name} ({country})'.format(name=self.name, country=self.get_country_display()) def get_absolute_url(self): + """Return the Institution detail page.""" return reverse('affiliations:institution_details', args=(self.id,)) def contributors(self): + """All Contributor instances related to the Institution.""" return Contributor.objects.filter(affiliations__institution=self) diff --git a/affiliations/templates/affiliations/base.html b/affiliations/templates/affiliations/base.html new file mode 100644 index 0000000000000000000000000000000000000000..6e6300f5ffebd97a0a4a9cc57c02baa65e50dccf --- /dev/null +++ b/affiliations/templates/affiliations/base.html @@ -0,0 +1,13 @@ +{% extends 'scipost/base.html' %} + +{% block breadcrumb %} + <div class="container-outside header"> + <div class="container"> + <nav class="breadcrumb hidden-sm-down"> + {% block breadcrumb_items %} + <a href="{% url 'affiliations:institutions' %}" class="breadcrumb-item">Institutions</a> + {% endblock %} + </nav> + </div> + </div> +{% endblock %} diff --git a/affiliations/templates/affiliations/institution_detail.html b/affiliations/templates/affiliations/institution_detail.html new file mode 100644 index 0000000000000000000000000000000000000000..641f629292c7e8d000698fcb847373ea3e9af177 --- /dev/null +++ b/affiliations/templates/affiliations/institution_detail.html @@ -0,0 +1,26 @@ +{% extends 'affiliations/base.html' %} + +{% load bootstrap %} + +{% block pagetitle %}: Institution details{% endblock pagetitle %} + +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">{{ institution }}</span> +{% endblock %} + +{% block content %} + +<h1 class="highlight">Institution {{ institution }}</h1> + +<ul> + {% for publication in institution.publications.all %} + <li> + <a href="{{ publication.get_absolute_url }}">{{ publication.title }}</a> + <br>by {{ publication.author_list }}, + <br>{{ publication.citation }} + </li> + {% endfor %} +</ul> + +{% endblock content %} diff --git a/affiliations/templates/affiliations/institution_form.html b/affiliations/templates/affiliations/institution_form.html index d6f6c8fb38c08b764e03fe5a85564daea0ce6d9a..a1b2c6f835eb8213aa49f8744c3903acc4dbe318 100644 --- a/affiliations/templates/affiliations/institution_form.html +++ b/affiliations/templates/affiliations/institution_form.html @@ -1,4 +1,4 @@ -{% extends 'scipost/_personal_page_base.html' %} +{% extends 'affiliations/base.html' %} {% load bootstrap %} @@ -6,7 +6,6 @@ {% block breadcrumb_items %} {{ block.super }} - <a href="{% url 'affiliations:institutions' %}" class="breadcrumb-item">Institutions</a> <span class="breadcrumb-item">Institution detail</span> {% endblock %} diff --git a/affiliations/templates/affiliations/institution_list.html b/affiliations/templates/affiliations/institution_list.html index 50d0b8ea36fd872feb75a211c4cf652822c9bd47..a76c137dd6e2ba85102ca14a1f173535d6fab87e 100644 --- a/affiliations/templates/affiliations/institution_list.html +++ b/affiliations/templates/affiliations/institution_list.html @@ -1,23 +1,29 @@ -{% extends 'scipost/_personal_page_base.html' %} +{% extends 'affiliations/base.html' %} {% block pagetitle %}: Institutions{% endblock pagetitle %} + {% block breadcrumb_items %} - {{ block.super }} <span class="breadcrumb-item">Institutions</span> {% endblock %} {% block content %} -<h1>All Institutions in the database</h1> +<h1 class="highlight">Institutions</h1> + +<h3>All Institutions with a SciPost publication</h3> {% if is_paginated %} {% include 'partials/pagination.html' with page_obj=page_obj %} {% endif %} + <ul> {% for institution in object_list %} - <li><a href="{% url 'affiliations:institution_details' institution.id %}">{{ institution }}</a></li> + <li> + <a href="{{ institution.get_absolute_url }}">{{ institution }}</a> + {% if perms.scipost.can_manage_affiliations %} · <a href="{% url 'affiliations:institution_edit' institution.id %}"><i class="fa fa-pencil"></i></a>{% endif %} + </li> {% empty %} <li><em>There are no Institutions known yet.</em><li> {% endfor %} diff --git a/affiliations/urls.py b/affiliations/urls.py index 0b839a4656c5434cacde2f183db38203ed1182fc..22b6fc62eb3081d47741c2169c86467015116523 100644 --- a/affiliations/urls.py +++ b/affiliations/urls.py @@ -8,8 +8,10 @@ from . import views urlpatterns = [ url(r'^$', views.InstitutionListView.as_view(), name='institutions'), - url(r'^(?P<institution_id>[0-9]+)/$', views.InstitutionUpdateView.as_view(), + url(r'^(?P<institution_id>[0-9]+)/$', views.InstitutionDetailView.as_view(), name='institution_details'), + url(r'^(?P<institution_id>[0-9]+)/edit', views.InstitutionUpdateView.as_view(), + name='institution_edit'), url(r'^(?P<institution_id>[0-9]+)/merge$', views.merge_institutions, name='merge_institutions'), ] diff --git a/affiliations/views.py b/affiliations/views.py index 83e94a2c94c16ab8d865b246803bdc04c6d9ab47..9de5a4d024652bae71457646716549a6560fe995 100644 --- a/affiliations/views.py +++ b/affiliations/views.py @@ -7,6 +7,7 @@ from django.contrib import messages from django.contrib.auth.decorators import permission_required from django.urls import reverse from django.utils.decorators import method_decorator +from django.views.generic.detail import DetailView from django.views.generic.edit import UpdateView from django.views.generic.list import ListView from django.shortcuts import get_object_or_404 @@ -15,10 +16,14 @@ from .forms import InstitutionMergeForm from .models import Institution -@method_decorator(permission_required('scipost.can_manage_affiliations'), name='dispatch') class InstitutionListView(ListView): + queryset = Institution.objects.has_publications() + paginate_by = 20 + + +class InstitutionDetailView(DetailView): model = Institution - paginate_by = 100 + pk_url_kwarg = 'institution_id' @method_decorator(permission_required('scipost.can_manage_affiliations'), name='dispatch') @@ -53,4 +58,4 @@ def merge_institutions(request, institution_id): messages.success(request, 'Institution {a} merged into {b}'.format( a=form.cleaned_data.get('institution', '?'), b=institution)) - return redirect(reverse('affiliations:institution_details', args=(institution.id,))) + return redirect(reverse('affiliations:institution_edit', args=(institution.id,))) diff --git a/funders/managers.py b/funders/managers.py new file mode 100644 index 0000000000000000000000000000000000000000..12d206fbfbd2615bfca021d1b008fe64ed7197b0 --- /dev/null +++ b/funders/managers.py @@ -0,0 +1,12 @@ +__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from django.db import models + + +class FunderQuerySet(models.QuerySet): + def has_publications(self): + """Return those Funder instances related to any Publication instance.""" + return self.filter( + models.Q(publications__isnull=False) | models.Q(grants__publications__isnull=False)) diff --git a/funders/migrations/0003_auto_20180425_2146.py b/funders/migrations/0003_auto_20180425_2146.py new file mode 100644 index 0000000000000000000000000000000000000000..b1f2aa55e96a7df2a9d7018efa6b6298785147d7 --- /dev/null +++ b/funders/migrations/0003_auto_20180425_2146.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-04-25 19:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0002_auto_20171229_1435'), + ] + + operations = [ + migrations.AlterField( + model_name='funder', + name='acronym', + field=models.CharField(blank=True, default='', max_length=32), + ), + ] diff --git a/funders/migrations/0004_auto_20180425_2146.py b/funders/migrations/0004_auto_20180425_2146.py new file mode 100644 index 0000000000000000000000000000000000000000..a929cb51fd082773ae15c7221046270fa83be19d --- /dev/null +++ b/funders/migrations/0004_auto_20180425_2146.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-04-25 19:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0003_auto_20180425_2146'), + ] + + operations = [ + migrations.AlterField( + model_name='funder', + name='acronym', + field=models.CharField(blank=True, max_length=32), + ), + ] diff --git a/funders/migrations/0005_auto_20180425_2211.py b/funders/migrations/0005_auto_20180425_2211.py new file mode 100644 index 0000000000000000000000000000000000000000..0ed56330406865e3630eec772710cefc37bc016b --- /dev/null +++ b/funders/migrations/0005_auto_20180425_2211.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-04-25 20:11 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0004_auto_20180425_2146'), + ] + + operations = [ + migrations.AlterField( + model_name='grant', + name='funder', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='grants', to='funders.Funder'), + ), + migrations.AlterField( + model_name='grant', + name='further_details', + field=models.CharField(blank=True, default='', max_length=256), + ), + migrations.AlterField( + model_name='grant', + name='recipient', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='grants', to='scipost.Contributor'), + ), + migrations.AlterField( + model_name='grant', + name='recipient_name', + field=models.CharField(blank=True, default='', max_length=64), + ), + ] diff --git a/funders/migrations/0006_auto_20180425_2212.py b/funders/migrations/0006_auto_20180425_2212.py new file mode 100644 index 0000000000000000000000000000000000000000..bc846c3962a333dce84a0d59ed9f0e99a72b681f --- /dev/null +++ b/funders/migrations/0006_auto_20180425_2212.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-04-25 20:12 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('funders', '0005_auto_20180425_2211'), + ] + + operations = [ + migrations.AlterField( + model_name='grant', + name='further_details', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AlterField( + model_name='grant', + name='recipient_name', + field=models.CharField(blank=True, max_length=64), + ), + ] diff --git a/funders/models.py b/funders/models.py index 93410a0fe07f5e281309e0bcb32e8c84cfe761b6..ceefe67b5ec091fbfd4240e11ab90b604279bafa 100644 --- a/funders/models.py +++ b/funders/models.py @@ -8,16 +8,21 @@ from django.urls import reverse from journals.models import Publication +from .managers import FunderQuerySet + class Funder(models.Model): - """ + """Funder is a Fundref regsitry. + Funding info metadata is linked to funders from Crossref's - Fundref registry. """ + name = models.CharField(max_length=256) - acronym = models.CharField(max_length=32, blank=True, null=True) + acronym = models.CharField(max_length=32, blank=True) identifier = models.CharField(max_length=200, unique=True) + objects = FunderQuerySet.as_manager() + class Meta: ordering = ['name', 'acronym'] @@ -28,27 +33,31 @@ class Funder(models.Model): return result def get_absolute_url(self): + """Return the Funder detail page.""" return reverse('funders:funder_publications', args=(self.id,)) def all_related_publications(self): + """Return all Publication objects linked to this Funder.""" return Publication.objects.filter( Q(funders_generic=self) | Q(grants__funder=self)).distinct() class Grant(models.Model): - """ - An instance of a grant, award or other funding. + """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('funders.Funder', on_delete=models.CASCADE) number = models.CharField(max_length=64) - recipient_name = models.CharField(max_length=64, blank=True, null=True) + recipient_name = models.CharField(max_length=64, blank=True) recipient = models.ForeignKey('scipost.Contributor', blank=True, null=True, on_delete=models.CASCADE) - further_details = models.CharField(max_length=256, blank=True, null=True) + further_details = models.CharField(max_length=256, blank=True) class Meta: + default_related_name = 'grants' ordering = ['funder', 'recipient', 'recipient_name', 'number'] unique_together = ('funder', 'number') diff --git a/funders/templates/funders/base.html b/funders/templates/funders/base.html new file mode 100644 index 0000000000000000000000000000000000000000..b36e0fc49f13e65cb6024b6183a05889f0f4c8fa --- /dev/null +++ b/funders/templates/funders/base.html @@ -0,0 +1,13 @@ +{% extends 'scipost/base.html' %} + +{% block breadcrumb %} + <div class="container-outside header"> + <div class="container"> + <nav class="breadcrumb hidden-sm-down"> + {% block breadcrumb_items %} + <a href="{% url 'funders:funders' %}" class="breadcrumb-item">Funders</a> + {% endblock %} + </nav> + </div> + </div> +{% endblock %} diff --git a/funders/templates/funders/funder_details.html b/funders/templates/funders/funder_details.html index 382cb69bb5ec80832975e65ba26e9bf61f000bd5..9fd9b51b26bfd08ea977e54263ef1a86d481b053 100644 --- a/funders/templates/funders/funder_details.html +++ b/funders/templates/funders/funder_details.html @@ -1,16 +1,25 @@ -{% extends 'scipost/base.html' %} +{% extends 'funders/base.html' %} {% block pagetitle %}: Funder details{% endblock pagetitle %} +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">{{ funder.name }}</span> +{% endblock %} + {% block content %} <h1 class="highlight">Funder {{ funder.name }}</h1> -<h3>All Publications related to this Funder</h3> +<h3>All Publications related to {{ funder.name }}</h3> <ul> {% for publication in funder.all_related_publications %} - <li><a href="{{ publication.get_absolute_url }}">{{ publication }}</a></li> + <li> + <a href="{{ publication.get_absolute_url }}">{{ publication.title }}</a> + <br>by {{ publication.author_list }}, + <br>{{ publication.citation }} + </li> {% empty %} <li>No publications</li> {% endfor %} diff --git a/funders/templates/funders/funder_list.html b/funders/templates/funders/funder_list.html new file mode 100644 index 0000000000000000000000000000000000000000..ac4b9599470ee692c81d348970595963f9d90198 --- /dev/null +++ b/funders/templates/funders/funder_list.html @@ -0,0 +1,29 @@ +{% extends 'funders/base.html' %} + +{% block pagetitle %}: Funders list{% endblock pagetitle %} + +{% block breadcrumb_items %} + <span class="breadcrumb-item">Funders</span> +{% endblock %} + +{% block content %} + +<h1 class="highlight">Funders</h1> + +{% if perms.scipost.can_view_all_funding_info %} + <a href="{% url 'funders:funders_dashboard' %}">Go to dashboard</a> + <br><br> +{% endif %} + +<h3>All Funders with a SciPost publication</h3> + +<ul> + {% for funder in funders %} + <li><a href="{{ funder.get_absolute_url }}">{{ funder }}</a></li> + {% empty %} + <li>No funders</li> + {% endfor %} +</ul> + + +{% endblock content %} diff --git a/funders/templates/funders/funders.html b/funders/templates/funders/funders_dashboard.html similarity index 94% rename from funders/templates/funders/funders.html rename to funders/templates/funders/funders_dashboard.html index 0ab70cf7f9e3769407ee16b5fbf14ef0765ac7d9..16ea8e04a2b36caa4767ce575c9196d6126a1492 100644 --- a/funders/templates/funders/funders.html +++ b/funders/templates/funders/funders_dashboard.html @@ -1,6 +1,11 @@ -{% extends 'scipost/base.html' %} +{% extends 'funders/base.html' %} -{% block pagetitle %}: Funders{% endblock pagetitle %} +{% block pagetitle %}: Funders dashboard{% endblock pagetitle %} + +{% block breadcrumb_items %} + {{ block.super }} + <span class="breadcrumb-item">Dashboard</span> +{% endblock %} {% load bootstrap %} diff --git a/funders/urls.py b/funders/urls.py index 8ec6892cddbbc9a46582790535020b16c2f04190..48f99d288374cd7faded49940a731a2604568833 100644 --- a/funders/urls.py +++ b/funders/urls.py @@ -8,10 +8,10 @@ from . import views urlpatterns = [ url(r'^$', views.funders, name='funders'), + url(r'^dashboard$', views.funders_dashboard, name='funders_dashboard'), url(r'^query_crossref_for_funder$', views.query_crossref_for_funder, name='query_crossref_for_funder'), url(r'^add$', views.add_funder, name='add_funder'), - url(r'^(?P<funder_id>[0-9]+)/$', views.funder_publications, - name='funder_publications'), + url(r'^(?P<funder_id>[0-9]+)/$', views.funder_publications, name='funder_publications'), url(r'^grants/add$', views.CreateGrantView.as_view(), name='add_grant'), ] diff --git a/funders/views.py b/funders/views.py index 5e04d384fe374d608c3fe868ce0255d17307527d..f6b2d5b56af91fda9bcfe07df1596e76cfc660cb 100644 --- a/funders/views.py +++ b/funders/views.py @@ -20,14 +20,15 @@ from scipost.mixins import PermissionsMixin @permission_required('scipost.can_view_all_funding_info', raise_exception=True) -def funders(request): +def funders_dashboard(request): + """Administration of Funders and Grants.""" funders = Funder.objects.all() form = FunderRegistrySearchForm() grants = Grant.objects.all() grant_form = GrantForm(request=request) context = {'form': form, 'funders': funders, 'grants': grants, 'grant_form': grant_form} - return render(request, 'funders/funders.html', context) + return render(request, 'funders/funders_dashboard.html', context) @permission_required('scipost.can_view_all_funding_info', raise_exception=True) @@ -59,13 +60,20 @@ def add_funder(request): str(funder)) elif form.has_changed(): messages.warning(request, 'The form was invalidly filled.') - return redirect(reverse('funders:funders')) + return redirect(reverse('funders:funders_dashboard')) + + +def funders(request): + """List page of Funders.""" + funders = Funder.objects.has_publications().distinct() + context = { + 'funders': funders + } + return render(request, 'funders/funder_list.html', context) def funder_publications(request, funder_id): - """ - See details of specific Funder (publicly accessible). - """ + """Detail page of a specific Funder (publicly accessible).""" funder = get_object_or_404(Funder, id=funder_id) context = {'funder': funder} return render(request, 'funders/funder_details.html', context) @@ -91,4 +99,4 @@ class CreateGrantView(PermissionsMixin, HttpRefererMixin, CreateView): permission_required = 'scipost.can_create_grants' model = Grant form_class = GrantForm - success_url = reverse_lazy('funders:funders') + success_url = reverse_lazy('funders:funders_dashboard') diff --git a/journals/templates/journals/manage_metadata.html b/journals/templates/journals/manage_metadata.html index bcaa4e0c9daaf8e0c5ddaf74f95195bfdb1f4327..0d240c96c2169ffb44cecf3df8ee4ade5f474373 100644 --- a/journals/templates/journals/manage_metadata.html +++ b/journals/templates/journals/manage_metadata.html @@ -150,7 +150,7 @@ event: "focusin" <br/> <h3>Other funding-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> + <li><a href="{% url 'funders:funders_dashboard' %}" target="_blank">go to the Funders page to add a Funder and/or Grant instance</a></li> </ul> </div> </div> diff --git a/journals/templates/journals/publication_detail.html b/journals/templates/journals/publication_detail.html index 67892330e77d5b979ec472bfd0b120cc4136fc55..4a92ac026cb4fded076296c9b129b702992bb9c1 100644 --- a/journals/templates/journals/publication_detail.html +++ b/journals/templates/journals/publication_detail.html @@ -107,30 +107,22 @@ {% endfor %} </ul> + {% if publication.funders_generic.all %} + <h3>Funder{{ publication.funders_generic.count|pluralize }} for this publication</h3> + <ul> + {% for funder in publication.funders_generic.all %} + <li><a href="{{ funder.get_absolute_url }}">{{ funder }}</a></li> + {% endfor %} + </ul> + {% endif %} - - {% if is_edcol_admin %} - {# 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> - {% for funder in publication.funders_generic.all %} - <li><a href="{{ funder.get_absolute_url }}">{{ funder }}</a></li> - {% endfor %} - </ul> - {% endif %} - - {% if publication.institutions.exists %} - <h3>Institution{{ publication.institutions.count|pluralize }} related to this Publication:</h3> - <ul> - {% for institution in publication.institutions.all %} - <li>{{ institution }}</li> - {% endfor %} - </ul> - {% endif %} + {% if publication.institutions.all %} + <h3>Affiliation{{ publication.institutions.count|pluralize }} related to this Publication</h3> + <ul> + {% for institution in publication.institutions.all %} + <li><a href="{{ institution.get_absolute_url }}">{{ institution }}</a></li> + {% endfor %} + </ul> {% endif %} </div> </div> diff --git a/notifications/templates/notifications/partials/notification_list_popover.html b/notifications/templates/notifications/partials/notification_list_popover.html index 89f08094fdb87affd7f7d7f830c5695c988ffe0a..3f16c462650a4c636afde7c90ebd8d78bf609d74 100644 --- a/notifications/templates/notifications/partials/notification_list_popover.html +++ b/notifications/templates/notifications/partials/notification_list_popover.html @@ -31,7 +31,7 @@ {% endif %} {% if perms.scipost.can_view_all_funding_info %} - <a class="item {% active 'funders:funders' %}" href="{% url 'funders:funders' %}">Funders</a> + <a class="item {% active 'funders:funders_dashboard' %}" href="{% url 'funders:funders_dashboard' %}">Funders</a> {% endif %} {% if perms.scipost.can_view_production %}