From a214df63d29bdb21d321b6059bbd9d6cf65f5628 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Wed, 27 Jun 2018 22:34:54 +0200
Subject: [PATCH] Initiate work on ProspectiveFellows facilities

---
 colleges/admin.py                             | 12 +++-
 colleges/constants.py                         | 45 ++++++++++++
 colleges/forms.py                             | 10 ++-
 ...rospectivefellow_prospectivefellowevent.py | 48 +++++++++++++
 colleges/models.py                            | 53 +++++++++++++-
 .../colleges/_prospectivefellow_card.html     | 27 +++++++
 .../colleges/_prospectivefellow_event_li.html |  6 ++
 .../prospectivefellow_confirm_delete.html     | 25 +++++++
 .../colleges/prospectivefellow_form.html      | 16 +++++
 .../colleges/prospectivefellow_list.html      | 49 +++++++++++++
 colleges/urls.py                              | 10 +++
 colleges/views.py                             | 70 ++++++++++++++++++-
 12 files changed, 364 insertions(+), 7 deletions(-)
 create mode 100644 colleges/constants.py
 create mode 100644 colleges/migrations/0003_prospectivefellow_prospectivefellowevent.py
 create mode 100644 colleges/templates/colleges/_prospectivefellow_card.html
 create mode 100644 colleges/templates/colleges/_prospectivefellow_event_li.html
 create mode 100644 colleges/templates/colleges/prospectivefellow_confirm_delete.html
 create mode 100644 colleges/templates/colleges/prospectivefellow_form.html
 create mode 100644 colleges/templates/colleges/prospectivefellow_list.html

diff --git a/colleges/admin.py b/colleges/admin.py
index 8ff31a139..31c6ef976 100644
--- a/colleges/admin.py
+++ b/colleges/admin.py
@@ -4,7 +4,7 @@ __license__ = "AGPL v3"
 
 from django.contrib import admin
 
-from .models import Fellowship
+from .models import Fellowship, ProspectiveFellow, ProspectiveFellowEvent
 
 
 def fellowhip_is_active(fellowship):
@@ -20,3 +20,13 @@ class FellowshipAdmin(admin.ModelAdmin):
 
 
 admin.site.register(Fellowship, FellowshipAdmin)
+
+
+class ProspectiveFellowEventInline(admin.TabularInline):
+    model = ProspectiveFellowEvent
+
+class ProspectiveFellowAdmin(admin.ModelAdmin):
+    inlines = (ProspectiveFellowEventInline,)
+    search_fields = ['last_name', 'email']
+
+admin.site.register(ProspectiveFellow, ProspectiveFellowAdmin)
diff --git a/colleges/constants.py b/colleges/constants.py
new file mode 100644
index 000000000..a5d3cc8f4
--- /dev/null
+++ b/colleges/constants.py
@@ -0,0 +1,45 @@
+__copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+PROSPECTIVE_FELLOW_IDENTIFIED = 'identified'
+PROSPECTIVE_FELLOW_INVITED = 'invited'
+PROSPECTIVE_FELLOW_REINVITED = 'reinvited'
+PROSPECTIVE_FELLOW_MULTIPLY_REINVITED = 'multiplyreinvited'
+PROSPECTIVE_FELLOW_DECLINED = 'declined'
+PROSPECTIVE_FELLOW_UNRESPONSIVE = 'unresponsive'
+PROSPECTIVE_FELLOW_RETIRED = 'retired'
+PROSPECTIVE_FELLOW_DECEASED = 'deceased'
+PROSPECTIVE_FELLOW_INTERESTED = 'interested'
+PROSPECTIVE_FELLOW_REGISTERED = 'registered'
+PROSPECTIVE_FELLOW_ACTIVE_IN_COLLEGE = 'activeincollege'
+PROSPECTIVE_FELLOW_SCIPOST_EMERITUS = 'emeritus'
+
+PROSPECTIVE_FELLOW_STATUSES = (
+    (PROSPECTIVE_FELLOW_IDENTIFIED, 'Identified as potential Fellow'),
+    (PROSPECTIVE_FELLOW_INVITED, 'Invited to become Fellow'),
+    (PROSPECTIVE_FELLOW_REINVITED, 'Reinvited after initial invitation'),
+    (PROSPECTIVE_FELLOW_MULTIPLY_REINVITED, 'Multiply reinvited'),
+    (PROSPECTIVE_FELLOW_DECLINED, 'Declined the invitation'),
+    (PROSPECTIVE_FELLOW_UNRESPONSIVE, 'Marked as unresponsive'),
+    (PROSPECTIVE_FELLOW_RETIRED, 'Retired'),
+    (PROSPECTIVE_FELLOW_DECEASED, 'Deceased'),
+    (PROSPECTIVE_FELLOW_INTERESTED, 'Marked as interested, Fellowship being set up'),
+    (PROSPECTIVE_FELLOW_REGISTERED, 'Registered as Contributor'),
+    (PROSPECTIVE_FELLOW_ACTIVE_IN_COLLEGE, 'Currently active in a College'),
+    (PROSPECTIVE_FELLOW_SCIPOST_EMERITUS, 'SciPost Emeritus'),
+)
+
+PROSPECTIVE_FELLOW_EVENT_DEFINED = 'defined'
+PROSPECTIVE_FELLOW_EVENT_EMAILED = 'emailed'
+PROSPECTIVE_FELLOW_EVENT_RESPONDED = 'responded'
+PROSPECTIVE_FELLOW_EVENT_COMMENT = 'comment'
+PROSPECTIVE_FELLOW_EVENT_DEACTIVATION = 'deactivation'
+
+PROSPECTIVE_FELLOW_EVENTS = (
+    (PROSPECTIVE_FELLOW_EVENT_DEFINED, 'Defined in database'),
+    (PROSPECTIVE_FELLOW_EVENT_EMAILED, 'Emailed with invitation'),
+    (PROSPECTIVE_FELLOW_EVENT_RESPONDED, 'Response received'),
+    (PROSPECTIVE_FELLOW_EVENT_COMMENT, 'Comment'),
+    (PROSPECTIVE_FELLOW_EVENT_DEACTIVATION, 'Deactivation: not considered anymore'),
+)
diff --git a/colleges/forms.py b/colleges/forms.py
index abeef20ba..9e122f940 100644
--- a/colleges/forms.py
+++ b/colleges/forms.py
@@ -10,7 +10,7 @@ from proceedings.models import Proceedings
 from submissions.models import Submission
 from scipost.models import Contributor
 
-from .models import Fellowship
+from .models import Fellowship, ProspectiveFellow
 
 
 class AddFellowshipForm(forms.ModelForm):
@@ -217,3 +217,11 @@ class FellowshipAddProceedingsForm(forms.ModelForm):
         fellowship = self.instance
         proceedings.fellowships.add(fellowship)
         return fellowship
+
+
+class ProspectiveFellowCreateForm(forms.ModelForm):
+
+    class Meta:
+        model = ProspectiveFellow
+        fields = ['title', 'first_name', 'last_name', 'email',
+                  'discipline', 'expertises', 'webpage', 'status', 'contributor']
diff --git a/colleges/migrations/0003_prospectivefellow_prospectivefellowevent.py b/colleges/migrations/0003_prospectivefellow_prospectivefellowevent.py
new file mode 100644
index 000000000..aeff42134
--- /dev/null
+++ b/colleges/migrations/0003_prospectivefellow_prospectivefellowevent.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-06-27 17:14
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import scipost.fields
+import scipost.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('scipost', '0014_auto_20180414_2218'),
+        ('colleges', '0002_auto_20171229_1435'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ProspectiveFellow',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(choices=[('PR', 'Prof.'), ('DR', 'Dr'), ('MR', 'Mr'), ('MRS', 'Mrs'), ('MS', 'Ms')], max_length=4)),
+                ('first_name', models.CharField(max_length=30)),
+                ('last_name', models.CharField(max_length=150)),
+                ('email', models.EmailField(max_length=254)),
+                ('discipline', models.CharField(choices=[('physics', 'Physics'), ('astrophysics', 'Astrophysics'), ('mathematics', 'Mathematics'), ('computerscience', 'Computer Science')], default='physics', max_length=20, verbose_name='Main discipline')),
+                ('expertises', scipost.fields.ChoiceArrayField(base_field=models.CharField(choices=[('Physics', (('Phys:AE', 'Atomic, Molecular and Optical Physics - Experiment'), ('Phys:AT', 'Atomic, Molecular and Optical Physics - Theory'), ('Phys:BI', 'Biophysics'), ('Phys:CE', 'Condensed Matter Physics - Experiment'), ('Phys:CT', 'Condensed Matter Physics - Theory'), ('Phys:FD', 'Fluid Dynamics'), ('Phys:GR', 'Gravitation, Cosmology and Astroparticle Physics'), ('Phys:HE', 'High-Energy Physics - Experiment'), ('Phys:HT', 'High-Energy Physics - Theory'), ('Phys:HP', 'High-Energy Physics - Phenomenology'), ('Phys:MP', 'Mathematical Physics'), ('Phys:NE', 'Nuclear Physics - Experiment'), ('Phys:NT', 'Nuclear Physics - Theory'), ('Phys:QP', 'Quantum Physics'), ('Phys:SM', 'Statistical and Soft Matter Physics'))), ('Astrophysics', (('Astro:GA', 'Astrophysics of Galaxies'), ('Astro:CO', 'Cosmology and Nongalactic Astrophysics'), ('Astro:EP', 'Earth and Planetary Astrophysics'), ('Astro:HE', 'High Energy Astrophysical Phenomena'), ('Astro:IM', 'Instrumentation and Methods for Astrophysics'), ('Astro:SR', 'Solar and Stellar Astrophysics'))), ('Mathematics', (('Math:AG', 'Algebraic Geometry'), ('Math:AT', 'Algebraic Topology'), ('Math:AP', 'Analysis of PDEs'), ('Math:CT', 'Category Theory'), ('Math:CA', 'Classical Analysis and ODEs'), ('Math:CO', 'Combinatorics'), ('Math:AC', 'Commutative Algebra'), ('Math:CV', 'Complex Variables'), ('Math:DG', 'Differential Geometry'), ('Math:DS', 'Dynamical Systems'), ('Math:FA', 'Functional Analysis'), ('Math:GM', 'General Mathematics'), ('Math:GN', 'General Topology'), ('Math:GT', 'Geometric Topology'), ('Math:GR', 'Group Theory'), ('Math:HO', 'History and Overview'), ('Math:IT', 'Information Theory'), ('Math:KT', 'K-Theory and Homology'), ('Math:LO', 'Logic'), ('Math:MP', 'Mathematical Physics'), ('Math:MG', 'Metric Geometry'), ('Math:NT', 'Number Theory'), ('Math:NA', 'Numerical Analysis'), ('Math:OA', 'Operator Algebras'), ('Math:OC', 'Optimization and Control'), ('Math:PR', 'Probability'), ('Math:QA', 'Quantum Algebra'), ('Math:RT', 'Representation Theory'), ('Math:RA', 'Rings and Algebras'), ('Math:SP', 'Spectral Theory'), ('Math:ST', 'Statistics Theory'), ('Math:SG', 'Symplectic Geometry'))), ('Computer Science', (('Comp:AI', 'Artificial Intelligence'), ('Comp:CC', 'Computational Complexity'), ('Comp:CE', 'Computational Engineering, Finance, and Science'), ('Comp:CG', 'Computational Geometry'), ('Comp:GT', 'Computer Science and Game Theory'), ('Comp:CV', 'Computer Vision and Pattern Recognition'), ('Comp:CY', 'Computers and Society'), ('Comp:CR', 'Cryptography and Security'), ('Comp:DS', 'Data Structures and Algorithms'), ('Comp:DB', 'Databases'), ('Comp:DL', 'Digital Libraries'), ('Comp:DM', 'Discrete Mathematics'), ('Comp:DC', 'Distributed, Parallel, and Cluster Computing'), ('Comp:ET', 'Emerging Technologies'), ('Comp:FL', 'Formal Languages and Automata Theory'), ('Comp:GL', 'General Literature'), ('Comp:GR', 'Graphics'), ('Comp:AR', 'Hardware Architecture'), ('Comp:HC', 'Human-Computer Interaction'), ('Comp:IR', 'Information Retrieval'), ('Comp:IT', 'Information Theory'), ('Comp:LG', 'Learning'), ('Comp:LO', 'Logic in Computer Science'), ('Comp:MS', 'Mathematical Software'), ('Comp:MA', 'Multiagent Systems'), ('Comp:MM', 'Multimedia'), ('Comp:NI', 'Networking and Internet Architecture'), ('Comp:NE', 'Neural and Evolutionary Computing'), ('Comp:NA', 'Numerical Analysis'), ('Comp:OS', 'Operating Systems'), ('Comp:OH', 'Other Computer Science'), ('Comp:PF', 'Performance'), ('Comp:PL', 'Programming Languages'), ('Comp:RO', 'Robotics'), ('Comp:SI', 'Social and Information Networks'), ('Comp:SE', 'Software Engineering'), ('Comp:SD', 'Sound'), ('Comp:SC', 'Symbolic Computation'), ('Comp:SY', 'Systems and Control')))], max_length=10), blank=True, null=True, size=None)),
+                ('webpage', models.URLField(blank=True)),
+                ('status', models.CharField(choices=[('identified', 'Identified as potential Fellow'), ('invited', 'Invited to become Fellow'), ('reinvited', 'Reinvited after initial invitation'), ('multiplyreinvited', 'Multiply reinvited'), ('declined', 'Declined the invitation'), ('unresponsive', 'Marked as unresponsive'), ('retired', 'Retired'), ('deceased', 'Deceased'), ('interested', 'Marked as interested, Fellowship being set up'), ('registered', 'Registered as Contributor'), ('activeincollege', 'Currently active in a College'), ('emeritus', 'SciPost Emeritus')], default='identified', max_length=32)),
+                ('contributor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='scipost.Contributor')),
+            ],
+            options={
+                'ordering': ['-last_name'],
+            },
+        ),
+        migrations.CreateModel(
+            name='ProspectiveFellowEvent',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('event', models.CharField(choices=[('defined', 'Defined in database'), ('emailed', 'Emailed with invitation'), ('responded', 'Response received'), ('comment', 'Comment'), ('deactivation', 'Deactivation: not considered anymore')], max_length=32)),
+                ('comments', models.TextField(blank=True)),
+                ('noted_on', models.DateTimeField(auto_now_add=True)),
+                ('noted_by', models.ForeignKey(blank=True, null=True, on_delete=models.SET(scipost.models.get_sentinel_user), to='scipost.Contributor')),
+                ('prosfellow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='colleges.ProspectiveFellow')),
+            ],
+        ),
+    ]
diff --git a/colleges/models.py b/colleges/models.py
index 312021771..b482bed84 100644
--- a/colleges/models.py
+++ b/colleges/models.py
@@ -7,10 +7,16 @@ import datetime
 from django.db import models
 from django.urls import reverse
 
-from scipost.behaviors import TimeStampedModel
-
+from .constants import PROSPECTIVE_FELLOW_STATUSES, PROSPECTIVE_FELLOW_IDENTIFIED,\
+    PROSPECTIVE_FELLOW_EVENTS
 from .managers import FellowQuerySet
 
+from scipost.behaviors import TimeStampedModel
+from scipost.constants import SCIPOST_DISCIPLINES, DISCIPLINE_PHYSICS,\
+    SCIPOST_SUBJECT_AREAS, TITLE_CHOICES
+from scipost.fields import ChoiceArrayField
+from scipost.models import get_sentinel_user, Contributor
+
 
 class Fellowship(TimeStampedModel):
     """A Fellowship gives access to the Submission Pool to Contributors.
@@ -58,3 +64,46 @@ class Fellowship(TimeStampedModel):
         elif not self.until_date:
             return today >= self.start_date
         return today >= self.start_date and today <= self.until_date
+
+
+
+class ProspectiveFellow(models.Model):
+    """
+    A ProspectiveFellow is somebody who has been identified as
+    a potential member of an Editorial College.
+    """
+    title = models.CharField(max_length=4, choices=TITLE_CHOICES)
+    first_name = models.CharField(max_length=30)
+    last_name = models.CharField(max_length=150)
+    email = models.EmailField()
+    discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES,
+                                  default=DISCIPLINE_PHYSICS, verbose_name='Main discipline')
+    expertises = ChoiceArrayField(
+        models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS),
+        blank=True, null=True)
+    webpage = models.URLField(blank=True)
+    status = models.CharField(max_length=32, choices=PROSPECTIVE_FELLOW_STATUSES,
+                              default=PROSPECTIVE_FELLOW_IDENTIFIED)
+    contributor = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE,
+                                    null=True, blank=True, related_name='+')
+
+    class Meta:
+        ordering = ['last_name']
+
+    def __str__(self):
+        return '%s, %s %s (%s)' % (self.last_name, self.get_title_display(), self.first_name,
+                                   self.get_status_display())
+
+
+class ProspectiveFellowEvent(models.Model):
+    prosfellow = models.ForeignKey('colleges.ProspectiveFellow', on_delete=models.CASCADE)
+    event = models.CharField(max_length=32, choices=PROSPECTIVE_FELLOW_EVENTS)
+    comments = models.TextField(blank=True)
+    noted_on = models.DateTimeField(auto_now_add=True)
+    noted_by = models.ForeignKey('scipost.Contributor',
+                                 on_delete=models.SET(get_sentinel_user),
+                                 blank=True, null=True)
+
+    def __str__(self):
+        return '%s, %s %s: %s' % (self.prosfellow.last_name, self.prosfellow.get_title_display(),
+                                  self.prosfellow.first_name, self.get_event_display())
diff --git a/colleges/templates/colleges/_prospectivefellow_card.html b/colleges/templates/colleges/_prospectivefellow_card.html
new file mode 100644
index 000000000..7b4aff424
--- /dev/null
+++ b/colleges/templates/colleges/_prospectivefellow_card.html
@@ -0,0 +1,27 @@
+{% load bootstrap %}
+
+<div class="card-body">
+  <div class="row">
+    <div class="col-6">
+      <a href="{% url 'colleges:prospective_Fellow_update' pk=prosfel.id %}">Update</a>
+    </div>
+    <div class="col-6">
+      <a href="{% url 'colleges:prospective_Fellow_delete' pk=prosfel.id %}">Delete</a>
+    </div>
+  </div>
+
+  <div class="row">
+    <div class="col-md-6 ml-auto">
+      <h3>Events</h3>
+      <ul>
+	{% for event in prosfel.prospectivefellowevent_set.all %}
+	{% include 'colleges/_prospectivefellow_event_li.html' with event=event %}
+	{% empty %}
+	<li>No events found.</li>
+	{% endfor %}
+      </ul>
+    </div>
+    <div class="col-md-5">
+      <h3>Add an event for this Prospective Fellow</h3>
+    </div>
+</div>
diff --git a/colleges/templates/colleges/_prospectivefellow_event_li.html b/colleges/templates/colleges/_prospectivefellow_event_li.html
new file mode 100644
index 000000000..f3f28e66a
--- /dev/null
+++ b/colleges/templates/colleges/_prospectivefellow_event_li.html
@@ -0,0 +1,6 @@
+<li id="{{ event.id }}">
+  <div class="font-weight-bold">{{ event.get_event_display }} <small class="text-muted">noted {{ event.noted_on }} {% if event.noted_by %}by {{ event.noted_by }}{% endif %}</small></div>
+  {% if event.comments %}
+  <div>{{ event.comments|linebreaks }}</div>
+  {% endif %}
+</li>
diff --git a/colleges/templates/colleges/prospectivefellow_confirm_delete.html b/colleges/templates/colleges/prospectivefellow_confirm_delete.html
new file mode 100644
index 000000000..cd80fe388
--- /dev/null
+++ b/colleges/templates/colleges/prospectivefellow_confirm_delete.html
@@ -0,0 +1,25 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Delete Prospective Fellow{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+    <div class="col-12">
+        <h1 class="highlight">Delete Prospective Fellow</h1>
+	{{ object }}
+    </div>
+</div>
+<div class="row">
+  <div class="col-12">
+      <form method="post">
+        {% csrf_token %}
+        <h3 class="mb-2">Are you sure you want to delete this Prospective Fellow?</h3>
+        <input type="submit" class="btn btn-danger" value="Yes, delete it" />
+      </form>
+    </ul>
+  </div>
+</div>
+
+{% endblock content %}
diff --git a/colleges/templates/colleges/prospectivefellow_form.html b/colleges/templates/colleges/prospectivefellow_form.html
new file mode 100644
index 000000000..31e3ee347
--- /dev/null
+++ b/colleges/templates/colleges/prospectivefellow_form.html
@@ -0,0 +1,16 @@
+{% extends 'scipost/base.html' %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Prospective Fellows{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+  <div class="col-12">
+    <form action="{% url 'colleges:prospective_Fellow_create' %}" method="post">
+      {% csrf_token %}
+      {{ form|bootstrap }}
+      <input type="submit" value="Submit" class="btn btn-primary">
+  </div>
+</div>
+{% endblock content %}
diff --git a/colleges/templates/colleges/prospectivefellow_list.html b/colleges/templates/colleges/prospectivefellow_list.html
new file mode 100644
index 000000000..4d5ac386c
--- /dev/null
+++ b/colleges/templates/colleges/prospectivefellow_list.html
@@ -0,0 +1,49 @@
+{% extends 'scipost/base.html' %}
+
+{% load scipost_extras %}
+
+{% load bootstrap %}
+
+{% block pagetitle %}: Prospective Fellows{% endblock pagetitle %}
+
+{% block content %}
+<div class="row">
+  <div class="col-12">
+    <a href="{% url 'colleges:prospective_Fellow_create' %}">Add a Prospective Fellow</a>
+    <br/><br/>
+    <table class="table table-hover mb-5">
+      <thead class="thead-default">
+	<tr>
+	  <th>Name</th>
+	  <th>Discipline</th>
+	  <th>Expertises</th>
+	  <th>Status</th>
+	</tr>
+      </thead>
+      <tbody id="accordion" role="tablist" aria-multiselectable="true">
+	{% for prosfel in object_list %}
+	<tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ prosfel.id }}" aria-expanded="true" aria-controls="collapse{{ prosfel.id }}" style="cursor: pointer;">
+	  <td>{{ prosfel.last_name }}, {{ prosfel.get_title_display }} {{ prosfel.first_name }}</td>
+	  <td>{{ prosfel.get_discipline_display }}</td>
+	  <td>
+	    {% for expertise in prosfel.expertises %}
+            <div class="single d-inline" data-specialization="{{expertise|lower}}" data-toggle="tooltip" data-placement="bottom" title="{{expertise|get_specialization_display}}">{{expertise|get_specialization_code}}</div>
+	    {% endfor %}
+	  </td>
+	  <td>{{ prosfel.get_status_display }}</td>
+	</tr>
+	<tr id="collapse{{ prosfel.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ prosfel.id }}" style="background-color: #fff;">
+	  <td colspan="4">
+	    {% include 'colleges/_prospectivefellow_card.html' with prosfel=prosfel %}
+	  </td>
+	</tr>
+	{% empty %}
+	<tr>
+	  <td colspan="4">No Prospective Fellows found</td>
+	</tr>
+	{% endfor %}
+      </tbody>
+    </table>
+  </div>
+</div>
+{% endblock content %}
diff --git a/colleges/urls.py b/colleges/urls.py
index fa557c7f8..4c31ec0d9 100644
--- a/colleges/urls.py
+++ b/colleges/urls.py
@@ -42,4 +42,14 @@ urlpatterns = [
         views.fellowship_add_proceedings, name='fellowship_add_proceedings'),
     url(r'^fellowships/(?P<id>[0-9]+)/proceedings/(?P<proceedings_id>[0-9]+)/remove$',
         views.fellowship_remove_proceedings, name='fellowship_remove_proceedings'),
+
+    # Prospective Fellows
+    url(r'^prospectivefellows/$',
+        views.ProspectiveFellowListView.as_view(), name='prospective_Fellows'),
+    url(r'^prospectivefellows/add/$',
+        views.ProspectiveFellowCreateView.as_view(), name='prospective_Fellow_create'),
+    url(r'^prospectivefellows/(?P<pk>[0-9]+)/update/$',
+        views.ProspectiveFellowUpdateView.as_view(), name='prospective_Fellow_update'),
+    url(r'^prospectivefellows/(?P<pk>[0-9]+)/delete/$',
+        views.ProspectiveFellowDeleteView.as_view(), name='prospective_Fellow_delete'),
 ]
diff --git a/colleges/views.py b/colleges/views.py
index babe8a453..02ea1a107 100644
--- a/colleges/views.py
+++ b/colleges/views.py
@@ -5,15 +5,19 @@ __license__ = "AGPL v3"
 from django.contrib import messages
 from django.contrib.auth.decorators import login_required, permission_required
 from django.shortcuts import get_object_or_404, render, redirect
-from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse, reverse_lazy
+from django.utils.decorators import method_decorator
+from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.views.generic.list import ListView
 
 from submissions.models import Submission
 
 from .forms import FellowshipForm, FellowshipTerminateForm, FellowshipRemoveSubmissionForm,\
     FellowshipAddSubmissionForm, AddFellowshipForm, SubmissionAddFellowshipForm,\
     FellowshipRemoveProceedingsForm, FellowshipAddProceedingsForm, SubmissionAddVotingFellowForm,\
-    FellowVotingRemoveSubmissionForm
-from .models import Fellowship
+    FellowVotingRemoveSubmissionForm,\
+    ProspectiveFellowCreateForm
+from .models import Fellowship, ProspectiveFellow
 
 
 @login_required
@@ -292,3 +296,63 @@ def fellowship_add_proceedings(request, id):
         'form': form,
     }
     return render(request, 'colleges/fellowship_proceedings_add.html', context)
+
+
+
+
+@method_decorator(login_required, name='dispatch')
+@method_decorator(permission_required('scipost.can_manage_college_composition', raise_exception=True),
+                  name='dispatch')
+class ProspectiveFellowCreateView(CreateView):
+    """
+    Formview to create a new Prospective Fellow.
+    """
+    form_class = ProspectiveFellowCreateForm
+    template_name = 'colleges/prospectivefellow_form.html'
+    success_url = reverse_lazy('colleges:prospective_Fellows')
+
+
+@method_decorator(login_required, name='dispatch')
+@method_decorator(permission_required('scipost.can_manage_college_composition', raise_exception=True),
+                  name='dispatch')
+class ProspectiveFellowUpdateView(UpdateView):
+    """
+    Formview to update a Prospective Fellow.
+    """
+    model = ProspectiveFellow
+    form_class = ProspectiveFellowCreateForm
+    template_name = 'colleges/prospectivefellow_form.html'
+    success_url = reverse_lazy('colleges:prospective_Fellows')
+
+
+@method_decorator(login_required, name='dispatch')
+@method_decorator(permission_required('scipost.can_manage_college_composition', raise_exception=True),
+                  name='dispatch')
+class ProspectiveFellowDeleteView(DeleteView):
+    """
+    Delete a Prospective Fellow.
+    """
+    model = ProspectiveFellow
+    success_url = reverse_lazy('colleges:prospective_Fellows')
+
+
+@method_decorator(login_required, name='dispatch')
+@method_decorator(permission_required('scipost.can_manage_college_composition', raise_exception=True),
+                  name='dispatch')
+class ProspectiveFellowListView(ListView):
+    """
+    List the ProspectiveFellow object instances.
+    """
+    model = ProspectiveFellow
+    paginate_by = 50
+
+    def get_queryset(self):
+        """
+        Return a queryset of ProspectiveFellows using optional GET data.
+        """
+        queryset = ProspectiveFellow.objects.all()
+        if 'discipline' in self.request.GET:
+            queryset = queryset.filter(discipline=self.request.GET['discipline'])
+            if 'expertise' in self.request.GET:
+                queryset = queryset.filter(expertises__in=self.request.GET['expertise'])
+        return queryset
-- 
GitLab