From b025671b7ed2b395a45f9593aed5fb20251d9321 Mon Sep 17 00:00:00 2001 From: Jorran de Wit <jorrandewit@outlook.com> Date: Sun, 15 Oct 2017 22:19:50 +0200 Subject: [PATCH] Pool construction part 3 --- colleges/forms.py | 105 +++++++++++++++++ colleges/managers.py | 6 + colleges/migrations/0005_fellowship_guest.py | 20 ++++ colleges/models.py | 7 +- .../templates/colleges/fellowship_add.html | 24 ++++ .../colleges/fellowship_details.html | 19 ++- .../templates/colleges/fellowship_edit.html | 26 +++++ .../colleges/fellowship_submission_add.html | 26 +++++ .../fellowship_submission_remove.html | 29 +++++ colleges/templates/colleges/fellowships.html | 43 ++++++- colleges/urls.py | 15 ++- colleges/views.py | 110 +++++++++++++++++- submissions/forms.py | 7 ++ 13 files changed, 422 insertions(+), 15 deletions(-) create mode 100644 colleges/forms.py create mode 100644 colleges/migrations/0005_fellowship_guest.py create mode 100644 colleges/templates/colleges/fellowship_add.html create mode 100644 colleges/templates/colleges/fellowship_edit.html create mode 100644 colleges/templates/colleges/fellowship_submission_add.html create mode 100644 colleges/templates/colleges/fellowship_submission_remove.html diff --git a/colleges/forms.py b/colleges/forms.py new file mode 100644 index 000000000..d76f0c8c0 --- /dev/null +++ b/colleges/forms.py @@ -0,0 +1,105 @@ +import datetime + +from django import forms + +from submissions.models import Submission +from scipost.models import Contributor + +from .models import Fellowship + + +class AddFellowshipForm(forms.ModelForm): + class Meta: + model = Fellowship + fields = ( + 'guest', + 'contributor', + 'start_date', + 'until_date', + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['contributor'].queryset = Contributor.objects.fellows() + self.fields['contributor'].label = "Fellow" + + def clean(self): + start = self.cleaned_data.get('start_date') + until = self.cleaned_data.get('until_date') + if start and until: + if until <= start: + self.add_error('until_date', 'The given dates are not in chronological order.') + + +class FellowshipForm(forms.ModelForm): + class Meta: + model = Fellowship + fields = ( + 'guest', + 'start_date', + 'until_date', + ) + + def clean(self): + start = self.cleaned_data.get('start_date') + until = self.cleaned_data.get('until_date') + if start and until: + if until <= start: + self.add_error('until_date', 'The given dates are not in chronological order.') + + +class FellowshipTerminateForm(forms.ModelForm): + class Meta: + model = Fellowship + fields = [] + + def save(self): + today = datetime.date.today() + fellowship = self.instance + if fellowship.until_date > today: + fellowship.until_date = today + return fellowship.save() + + +class FellowshipRemoveSubmissionForm(forms.ModelForm): + """ + Use this form in admin-accessible views only! It could possibly reveal the + identity of the Editor-in-charge! + """ + class Meta: + model = Fellowship + fields = [] + + def __init__(self, *args, **kwargs): + self.submission = kwargs.pop('submission') + super().__init__(*args, **kwargs) + + def clean(self): + if self.submission.editor_in_charge == self.instance.contributor: + self.add_error(None, ('Submission cannot be removed as the Fellow is' + ' Editor-in-charge of this Submission.')) + + def save(self): + fellowship = self.instance + fellowship.pool.remove(self.submission) + return fellowship + + +class FellowshipAddSubmissionForm(forms.ModelForm): + submission = forms.ModelChoiceField(queryset=None, to_field_name='arxiv_identifier_w_vn_nr', + empty_label="Please choice the Submission to add to the pool") + + class Meta: + model = Fellowship + fields = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + pool = self.instance.pool.values_list('id', flat=True) + self.fields['submission'].queryset = Submission.objects.exclude(id__in=pool) + + def save(self): + submission = self.cleaned_data['submission'] + fellowship = self.instance + fellowship.pool.add(submission) + return fellowship diff --git a/colleges/managers.py b/colleges/managers.py index 9904333ac..224ebde3b 100644 --- a/colleges/managers.py +++ b/colleges/managers.py @@ -5,6 +5,12 @@ from django.db.models import Q class FellowQuerySet(models.QuerySet): + def guests(self): + return self.filter(guest=True) + + def regular(self): + return self.filter(guest=False) + def active(self): today = datetime.date.today() return self.filter( diff --git a/colleges/migrations/0005_fellowship_guest.py b/colleges/migrations/0005_fellowship_guest.py new file mode 100644 index 000000000..36a451c31 --- /dev/null +++ b/colleges/migrations/0005_fellowship_guest.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-10-15 20:08 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('colleges', '0004_remove_fellowship_affiliation'), + ] + + operations = [ + migrations.AddField( + model_name='fellowship', + name='guest', + field=models.BooleanField(default=False, verbose_name='Guest Fellowship'), + ), + ] diff --git a/colleges/models.py b/colleges/models.py index fd27a0679..8455d618a 100644 --- a/colleges/models.py +++ b/colleges/models.py @@ -21,13 +21,18 @@ class Fellowship(TimeStampedModel): start_date = models.DateField(null=True, blank=True) until_date = models.DateField(null=True, blank=True) + guest = models.BooleanField('Guest Fellowship', default=False) + objects = FellowQuerySet.as_manager() class Meta: unique_together = ('contributor', 'start_date', 'until_date') def __str__(self): - return self.contributor.__str__() + _str = self.contributor.__str__() + if self.guest: + _str += ' (guest fellowship)' + return _str def get_absolute_url(self): return reverse('colleges:fellowship', args=(self.id,)) diff --git a/colleges/templates/colleges/fellowship_add.html b/colleges/templates/colleges/fellowship_add.html new file mode 100644 index 000000000..76f1b2ec8 --- /dev/null +++ b/colleges/templates/colleges/fellowship_add.html @@ -0,0 +1,24 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} + +{% block breadcrumb_items %} + {{ block.super }} + <a href="{% url 'colleges:fellowships' %}" class="breadcrumb-item">Active Fellowships</a> + <span class="breadcrumb-item">Add Fellowship</span> +{% endblock %} + +{% block pagetitle %}: Add Fellowship{% endblock pagetitle %} + +{% block content %} + <h1>Add Fellowship</h1> + <br> + + <form method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-primary" type="submit" value="Add Fellowship"> + </form> + + +{% endblock %} diff --git a/colleges/templates/colleges/fellowship_details.html b/colleges/templates/colleges/fellowship_details.html index 793d9652f..efb823d1a 100644 --- a/colleges/templates/colleges/fellowship_details.html +++ b/colleges/templates/colleges/fellowship_details.html @@ -43,11 +43,18 @@ <th>Pool size</th> <td>{{ fellowship.pool.count }}</td> </tr> + <tr> + <th>Type</th> + <td>{{ fellowship.guest|yesno:"Guest fellowship,Regular fellowship"|safe }}</td> + </tr> </tbody> </table> - <a href="#" class="btn btn-danger">Terminate Fellowship</a> - <a href="#" class="btn btn-info ml-2">Edit Fellowship</a> + <form method="post" action="{% url 'colleges:fellowship_terminate' fellowship.id %}" class="d-inline"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Terminate Fellowship</button> + </form> + <a href="{% url 'colleges:fellowship_edit' fellowship.id %}" class="btn btn-info ml-2">Edit Fellowship</a> </div> <div class="col-md-6"> <h3>All fellowships of this fellow</h3> @@ -56,6 +63,7 @@ <thead> <tr> <th>Fellowship ID</th> + <th>Type</th> <th colspan="2">Date range</th> </tr> </thead> @@ -63,6 +71,7 @@ {% for fellowship in fellowship.sibling_fellowships %} <tr> <td>{{ fellowship.id }}</td> + <td>{{ fellowship.guest|yesno:"Guest fellowship,Regular fellowship"|safe }}</td> <td> {% if fellowship.start_date %} from {{ fellowship.start_date }} @@ -79,7 +88,7 @@ {% endfor %} </tbody> </table> - <a href="#">Add new Fellowship for {{ fellowship.contributor }}</a> + <a href="{% url 'colleges:fellowship_add' %}?contributor={{ fellowship.contributor.id }}">Add new Fellowship for {{ fellowship.contributor }}</a> </div> </div> @@ -102,13 +111,13 @@ {% if submission.editor_in_charge == fellowship.contributor %} <strong>Fellow is Editor-in-charge</strong> {% else %} - <a class="text-danger" href="#">Remove from this Fellowship's pool</a> + <a class="text-danger" href="{% url 'colleges:fellowship_remove_submission' fellowship.id submission.arxiv_identifier_w_vn_nr %}">Remove from this Fellowship's pool</a> {% endif %} </td> </tr> {% endfor %} <tr> - <td colspan="3" class="py-3 text-center"><a href="#">Add Submission to this Fellowship's pool</a></td> + <td colspan="3" class="py-3 text-center"><a href="{% url 'colleges:fellowship_add_submission' fellowship.id %}">Add Submission to this Fellowship's pool</a></td> </tr> </tbody> </table> diff --git a/colleges/templates/colleges/fellowship_edit.html b/colleges/templates/colleges/fellowship_edit.html new file mode 100644 index 000000000..809e85b64 --- /dev/null +++ b/colleges/templates/colleges/fellowship_edit.html @@ -0,0 +1,26 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} + +{% block breadcrumb_items %} + {{ block.super }} + <a href="{% url 'colleges:fellowships' %}" class="breadcrumb-item">Active Fellowships</a> + <a href="{{ fellowship.get_absolute_url }}" class="breadcrumb-item">Fellowship details</a> + <span class="breadcrumb-item">Edit Fellowship</span> +{% endblock %} + +{% block pagetitle %}: Edit Fellowship{% endblock pagetitle %} + +{% block content %} + <h1>Edit Fellowship</h1> + <h2 class="text-primary">{{ fellowship }}</h2> + <br> + + <form method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-primary" type="submit" value="Save"> + </form> + + +{% endblock %} diff --git a/colleges/templates/colleges/fellowship_submission_add.html b/colleges/templates/colleges/fellowship_submission_add.html new file mode 100644 index 000000000..36c617c15 --- /dev/null +++ b/colleges/templates/colleges/fellowship_submission_add.html @@ -0,0 +1,26 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} + +{% block breadcrumb_items %} + {{ block.super }} + <a href="{% url 'colleges:fellowships' %}" class="breadcrumb-item">Active Fellowships</a> + <a href="{{ fellowship.get_absolute_url }}" class="breadcrumb-item">Fellowship details</a> + <span class="breadcrumb-item">Add Submission to Pool</span> +{% endblock %} + +{% block pagetitle %}: Add Submission to Fellowship{% endblock pagetitle %} + +{% block content %} + <h1>Add Submission to Pool</h1> + <h2 class="text-primary">Fellowship {{ fellowship }}</h2> + <br> + + <form method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-primary px-3" type="submit" value="Add Submission"> + </form> + + +{% endblock %} diff --git a/colleges/templates/colleges/fellowship_submission_remove.html b/colleges/templates/colleges/fellowship_submission_remove.html new file mode 100644 index 000000000..c5defe25a --- /dev/null +++ b/colleges/templates/colleges/fellowship_submission_remove.html @@ -0,0 +1,29 @@ +{% extends 'submissions/admin/base.html' %} + +{% load bootstrap %} + +{% block breadcrumb_items %} + {{ block.super }} + <a href="{% url 'colleges:fellowships' %}" class="breadcrumb-item">Active Fellowships</a> + <a href="{{ fellowship.get_absolute_url }}" class="breadcrumb-item">Fellowship details</a> + <span class="breadcrumb-item">Remove Submission from Pool</span> +{% endblock %} + +{% block pagetitle %}: Remove Submission from Fellowship{% endblock pagetitle %} + +{% block content %} + <h1>Remove Submission from Pool</h1> + <h2 class="text-primary">Fellowship {{ fellowship }}</h2> + + <h3>Submission details</h3> + {% include 'submissions/_submission_summary_short.html' with submission=submission %} + <br> + + <form method="post"> + {% csrf_token %} + {{ form|bootstrap }} + <input class="btn btn-danger px-3" type="submit" value="Remove from Pool"> + </form> + + +{% endblock %} diff --git a/colleges/templates/colleges/fellowships.html b/colleges/templates/colleges/fellowships.html index 04111476f..ce1ef2db9 100644 --- a/colleges/templates/colleges/fellowships.html +++ b/colleges/templates/colleges/fellowships.html @@ -9,8 +9,9 @@ {% block content %} <h1>Active Regular Fellowships</h1> + <a href="{% url 'colleges:fellowship_add' %}">Add new Fellowship</a> - <table class="table"> + <table class="table mt-3"> <thead> <tr> <th>Fellow</th> @@ -21,16 +22,14 @@ </tr> </thead> <tbody> - {% for fellow in fellowships %} + {% for fellow in fellowships.regular %} <tr> - <td>{{ fellow }}</td> + <td>{{ fellow.contributor }}</td> <td>{{ fellow.contributor.get_discipline_display }}</td> <td>{{ fellow.start_date|default:'<i>No start date</i>' }}</td> <td>{{ fellow.until_date|default:'<i>No end date</i>' }}</td> <td> - <a href="{{ fellow.get_absolute_url }}">Edit Fellowship</a> - · - <a href="{{ fellow.get_absolute_url }}">View its pool</a> + <a href="{{ fellow.get_absolute_url }}">View Fellowship details</a> </td> </tr> {% empty %} @@ -40,4 +39,36 @@ {% endfor %} </tbody> </table> + + <h1>Active Guest Fellowships</h1> + <a href="{% url 'colleges:fellowship_add' %}?guest=1">Add new Guest Fellowship</a> + + <table class="table mt-3"> + <thead> + <tr> + <th>Fellow</th> + <th>Discipline</th> + <th>Start date</th> + <th>End date</th> + <th></th> + </tr> + </thead> + <tbody> + {% for fellow in fellowships.guests %} + <tr> + <td>{{ fellow.contributor }}</td> + <td>{{ fellow.contributor.get_discipline_display }}</td> + <td>{{ fellow.start_date|default:'<i>No start date</i>' }}</td> + <td>{{ fellow.until_date|default:'<i>No end date</i>' }}</td> + <td> + <a href="{{ fellow.get_absolute_url }}">View Fellowship details</a> + </td> + </tr> + {% empty %} + <tr> + <td class="py-2" colspan="4">There are no active guests fellowships</td> + </tr> + {% endfor %} + </tbody> + </table> {% endblock %} diff --git a/colleges/urls.py b/colleges/urls.py index d955c7d18..6ffb8a52c 100644 --- a/colleges/urls.py +++ b/colleges/urls.py @@ -1,9 +1,20 @@ from django.conf.urls import url +from submissions.constants import SUBMISSIONS_COMPLETE_REGEX + from . import views urlpatterns = [ - # Commentaries + # Fellowships url(r'^fellowships/$', views.fellowships, name='fellowships'), - url(r'^fellowships/(?P<id>[0-9]+)$', views.fellowship_detail, name='fellowship'), + url(r'^fellowships/add$', views.fellowship_add, name='fellowship_add'), + url(r'^fellowships/(?P<id>[0-9]+)/$', views.fellowship_detail, name='fellowship'), + url(r'^fellowships/(?P<id>[0-9]+)/edit$', views.fellowship_edit, name='fellowship_edit'), + url(r'^fellowships/(?P<id>[0-9]+)/terminate$', views.fellowship_terminate, + name='fellowship_terminate'), + url(r'^fellowships/(?P<id>[0-9]+)/submissions/{regex}/remove$'.format( + regex=SUBMISSIONS_COMPLETE_REGEX), views.fellowship_remove_submission, + name='fellowship_remove_submission'), + url(r'^fellowships/(?P<id>[0-9]+)/submissions/add$', + views.fellowship_add_submission, name='fellowship_add_submission'), ] diff --git a/colleges/views.py b/colleges/views.py index 1f2889a86..04e0467b3 100644 --- a/colleges/views.py +++ b/colleges/views.py @@ -1,6 +1,9 @@ +from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404, render, redirect +from .forms import FellowshipForm, FellowshipTerminateForm, FellowshipRemoveSubmissionForm,\ + FellowshipAddSubmissionForm, AddFellowshipForm from .models import Fellowship @@ -30,3 +33,108 @@ def fellowship_detail(request, id): 'fellowship': fellowship } return render(request, 'colleges/fellowship_details.html', context) + + +@login_required +@permission_required('scipost.can_manage_college_composition', raise_exception=True) +def fellowship_add(request): + """ + Create a new Fellowship. + """ + form = AddFellowshipForm(request.POST or None, initial=request.GET or None) + + if form.is_valid(): + fellowship = form.save() + messages.success(request, 'Fellowship added.') + return redirect(fellowship.get_absolute_url()) + + context = { + 'form': form + } + return render(request, 'colleges/fellowship_add.html', context) + + +@login_required +@permission_required('scipost.can_manage_college_composition', raise_exception=True) +def fellowship_edit(request, id): + """ + Edit basic information about fellowship. + """ + fellowship = get_object_or_404(Fellowship, id=id) + form = FellowshipForm(request.POST or None, instance=fellowship) + + if form.is_valid(): + form.save() + messages.success(request, 'Fellowship updated.') + return redirect(fellowship.get_absolute_url()) + + context = { + 'fellowship': fellowship, + 'form': form + } + return render(request, 'colleges/fellowship_edit.html', context) + + +@login_required +@permission_required('scipost.can_manage_college_composition', raise_exception=True) +def fellowship_terminate(request, id): + """ + Terminate Fellowship by setting the until_date to today's date. + """ + fellowship = get_object_or_404(Fellowship, id=id) + form = FellowshipTerminateForm(request.POST or None, instance=fellowship) + + if form.is_valid(): + form.save() + messages.success(request, 'Fellowship terminated.') + else: + messages.warning(request, 'Fellowship has not been terminated, please try again.') + return redirect(fellowship.get_absolute_url()) + + +@login_required +@permission_required('scipost.can_manage_college_composition', raise_exception=True) +def fellowship_remove_submission(request, id, arxiv_identifier_w_vn_nr): + """ + Remove Submission from the pool of a Fellowship. + """ + fellowship = get_object_or_404(Fellowship, id=id) + submission = get_object_or_404(fellowship.pool.all(), + arxiv_identifier_w_vn_nr=arxiv_identifier_w_vn_nr) + form = FellowshipRemoveSubmissionForm(request.POST or None, + submission=submission, instance=fellowship) + + if form.is_valid() and request.POST: + form.save() + messages.success(request, 'Submission {sub} removed from Fellowship.'.format( + sub=arxiv_identifier_w_vn_nr)) + return redirect(fellowship.get_absolute_url()) + + context = { + 'fellowship': fellowship, + 'form': form, + 'submission': submission + } + return render(request, 'colleges/fellowship_submission_remove.html', context) + + +@login_required +@permission_required('scipost.can_manage_college_composition', raise_exception=True) +def fellowship_add_submission(request, id): + """ + Add Submission to the pool of a Fellowship. + """ + fellowship = get_object_or_404(Fellowship, id=id) + form = FellowshipAddSubmissionForm(request.POST or None, instance=fellowship) + + if form.is_valid(): + form.save() + messages.success(request, 'Submission {submission} added to Fellowship.'.format( + submission=form.cleaned_data['submission'].arxiv_identifier_w_vn_nr)) + return redirect(fellowship.get_absolute_url()) + + context = { + 'fellowship': fellowship, + 'form': form, + } + return render(request, 'colleges/fellowship_submission_add.html', context) diff --git a/submissions/forms.py b/submissions/forms.py index ef88d0341..65bf13df8 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -17,6 +17,7 @@ from . import exceptions, helpers from .models import Submission, RefereeInvitation, Report, EICRecommendation, EditorialAssignment,\ iThenticateReport +from colleges.models import Fellowship from scipost.constants import SCIPOST_SUBJECT_AREAS from scipost.services import ArxivCaller from scipost.models import Contributor @@ -342,6 +343,11 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): submission.save() return submission + def set_pool(self, submission): + fellows = Fellowship.objects.active().regular().filter( + contributor__discipline=submission.discipline) + submission.pool.set(fellows) + @transaction.atomic def save(self): """ @@ -368,6 +374,7 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): submission = self.copy_and_save_data_from_resubmission(submission) submission = self.reassign_eic_and_admins(submission) submission.authors.add(self.requested_by.contributor) + self.set_pool(submission) return submission -- GitLab