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>
-                        &middot;
-                        <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