SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit a7e71f16 authored by Jean-Sébastien Caux's avatar Jean-Sébastien Caux
Browse files

Merge branch 'dev_JSC_20180627_ProspectiveFellows'

parents 135cd54f 787b985b
No related branches found
No related tags found
No related merge requests found
Showing
with 628 additions and 10 deletions
...@@ -4,7 +4,7 @@ __license__ = "AGPL v3" ...@@ -4,7 +4,7 @@ __license__ = "AGPL v3"
from django.contrib import admin from django.contrib import admin
from .models import Fellowship from .models import Fellowship, ProspectiveFellow, ProspectiveFellowEvent
def fellowhip_is_active(fellowship): def fellowhip_is_active(fellowship):
...@@ -20,3 +20,13 @@ class FellowshipAdmin(admin.ModelAdmin): ...@@ -20,3 +20,13 @@ class FellowshipAdmin(admin.ModelAdmin):
admin.site.register(Fellowship, FellowshipAdmin) 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)
__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_statuses_dict = dict(PROSPECTIVE_FELLOW_STATUSES)
PROSPECTIVE_FELLOW_EVENT_DEFINED = 'defined'
PROSPECTIVE_FELLOW_EVENT_EMAILED = 'emailed'
PROSPECTIVE_FELLOW_EVENT_RESPONDED = 'responded'
PROSPECTIVE_FELLOW_EVENT_STATUSUPDATED = 'statusupdated'
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_STATUSUPDATED, 'Status updated'),
(PROSPECTIVE_FELLOW_EVENT_COMMENT, 'Comment'),
(PROSPECTIVE_FELLOW_EVENT_DEACTIVATION, 'Deactivation: not considered anymore'),
)
...@@ -10,7 +10,7 @@ from proceedings.models import Proceedings ...@@ -10,7 +10,7 @@ from proceedings.models import Proceedings
from submissions.models import Submission from submissions.models import Submission
from scipost.models import Contributor from scipost.models import Contributor
from .models import Fellowship from .models import Fellowship, ProspectiveFellow, ProspectiveFellowEvent
class AddFellowshipForm(forms.ModelForm): class AddFellowshipForm(forms.ModelForm):
...@@ -217,3 +217,25 @@ class FellowshipAddProceedingsForm(forms.ModelForm): ...@@ -217,3 +217,25 @@ class FellowshipAddProceedingsForm(forms.ModelForm):
fellowship = self.instance fellowship = self.instance
proceedings.fellowships.add(fellowship) proceedings.fellowships.add(fellowship)
return fellowship return fellowship
class ProspectiveFellowForm(forms.ModelForm):
class Meta:
model = ProspectiveFellow
fields = ['title', 'first_name', 'last_name', 'email',
'discipline', 'expertises', 'webpage', 'status', 'contributor']
class ProspectiveFellowStatusForm(forms.ModelForm):
class Meta:
model = ProspectiveFellow
fields = ['status']
class ProspectiveFellowEventForm(forms.ModelForm):
class Meta:
model = ProspectiveFellowEvent
fields = ['event', 'comments']
# -*- 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')),
],
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-06-29 06:25
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('colleges', '0003_prospectivefellow_prospectivefellowevent'),
]
operations = [
migrations.AlterModelOptions(
name='prospectivefellow',
options={'ordering': ['last_name']},
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-07-01 19:10
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('colleges', '0004_auto_20180629_0825'),
]
operations = [
migrations.AlterField(
model_name='prospectivefellowevent',
name='noted_on',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2018-07-03 10:08
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('colleges', '0005_auto_20180701_2110'),
]
operations = [
migrations.AlterField(
model_name='prospectivefellowevent',
name='event',
field=models.CharField(choices=[('defined', 'Defined in database'), ('emailed', 'Emailed with invitation'), ('responded', 'Response received'), ('statusupdated', 'Status updated'), ('comment', 'Comment'), ('deactivation', 'Deactivation: not considered anymore')], max_length=32),
),
]
...@@ -6,11 +6,18 @@ import datetime ...@@ -6,11 +6,18 @@ import datetime
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from scipost.behaviors import TimeStampedModel from .constants import PROSPECTIVE_FELLOW_STATUSES, PROSPECTIVE_FELLOW_IDENTIFIED,\
PROSPECTIVE_FELLOW_EVENTS
from .managers import FellowQuerySet 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): class Fellowship(TimeStampedModel):
"""A Fellowship gives access to the Submission Pool to Contributors. """A Fellowship gives access to the Submission Pool to Contributors.
...@@ -58,3 +65,46 @@ class Fellowship(TimeStampedModel): ...@@ -58,3 +65,46 @@ class Fellowship(TimeStampedModel):
elif not self.until_date: elif not self.until_date:
return today >= self.start_date return today >= self.start_date
return today >= self.start_date and today <= self.until_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(default=timezone.now)
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())
{% load bootstrap %}
<div class="card-body">
<div class="row">
<div class="col-6">
<p>
{{ prosfel.last_name }}, {{ prosfel.get_title_display }} {{ prosfel.first_name }}
<br/>
{{ prosfel.email }}
<br/>
{% if prosfel.webpage %}<a href="{{ prosfel.webpage }}">webpage</a>
{% else %}No personal webpage given
{% endif %}
<br/>
{% if prosfel.contributor %}Associated to Contributor <a href="{{ prosfel.contributor.get_absolute_url }}">{{ prosfel.contributor }}</a></li>
{% else %}No associated Contributor
{% endif %}
</p>
</div>
<div class="col-6">
<ul>
<li><a href="{% url 'colleges:prospective_Fellow_update' pk=prosfel.id %}">Update</a> the data</li>
<li><a href="{% url 'colleges:prospective_Fellow_delete' pk=prosfel.id %}">Delete</a> this Prosepective Fellow</li>
<li><a href="{% url 'colleges:prospective_Fellow_email_initial' pk=prosfel.id %}">Prepare and send initial email</a></li>
</ul>
</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>Update the status of this Prospective Fellow</h3>
<form class="d-block mt-2 mb-3" action="{% url 'colleges:prospective_Fellow_update_status' pk=prosfel.id %}" method="post">
{% csrf_token %}
{{ pfstatus_form|bootstrap }}
<input type="submit" name="submit" value="Update status" class="btn btn-outline-secondary">
</form>
<hr/>
<h3>Add an event for this Prospective Fellow</h3>
<form class="d-block mt-2 mb-3" action="{% url 'colleges:prospective_Fellow_event_create' pk=prosfel.id %}" method="post">
{% csrf_token %}
{{ pfevent_form|bootstrap }}
<input type="submit" name="submit" value="Submit" class="btn btn-outline-secondary">
</form>
</div>
<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>
{% 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 %}
{% 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 %}
{% extends 'scipost/base.html' %}
{% load scipost_extras %}
{% load bootstrap %}
{% block pagetitle %}: Prospective Fellows{% endblock pagetitle %}
{% block content %}
<div class="row">
<div class="col-12">
<p>
<a href="{% url 'colleges:prospective_Fellows' %}">View all</a> or view by discipline/subject area:
{% for discipline in subject_areas %}
<a class="btn btn-primary" data-toggle="collapse" href="#collapse{{ discipline.0|cut:" " }}" role="button" aria-expanded="false" aria-controls="collapse{{ discipline.0|cut:" " }}">{{ discipline.0 }}</a>
{% endfor %}
</p>
{% for discipline in subject_areas %}
<div class="collapse" id="collapse{{ discipline.0|cut:" " }}">
<p>
{% for area in discipline.1 %}
<a href="{% url 'colleges:prospective_Fellows' %}?discipline={{ discipline.0|cut:" " }}&expertise={{ area.0 }}" method="get">{{ area.0 }}</a>
{% endfor %}
</p>
</div>
{% endfor %}
</div>
</div>
<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="false" 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 pfevent_form=pfevent_form %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="4">No Prospective Fellows found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}
...@@ -42,4 +42,41 @@ urlpatterns = [ ...@@ -42,4 +42,41 @@ urlpatterns = [
views.fellowship_add_proceedings, name='fellowship_add_proceedings'), views.fellowship_add_proceedings, name='fellowship_add_proceedings'),
url(r'^fellowships/(?P<id>[0-9]+)/proceedings/(?P<proceedings_id>[0-9]+)/remove$', url(r'^fellowships/(?P<id>[0-9]+)/proceedings/(?P<proceedings_id>[0-9]+)/remove$',
views.fellowship_remove_proceedings, name='fellowship_remove_proceedings'), 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]+)/update_status/$',
views.ProspectiveFellowUpdateStatusView.as_view(),
name='prospective_Fellow_update_status'
),
url(
r'^prospectivefellows/(?P<pk>[0-9]+)/delete/$',
views.ProspectiveFellowDeleteView.as_view(),
name='prospective_Fellow_delete'
),
url(
r'^prospectivefellows/(?P<pk>[0-9]+)/events/add$',
views.ProspectiveFellowEventCreateView.as_view(),
name='prospective_Fellow_event_create'
),
url(
r'^prospectivefellows/(?P<pk>[0-9]+)/email/$',
views.ProspectiveFellowInitialEmailView.as_view(),
name='prospective_Fellow_email_initial'
),
] ]
...@@ -5,15 +5,30 @@ __license__ = "AGPL v3" ...@@ -5,15 +5,30 @@ __license__ = "AGPL v3"
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import get_object_or_404, render, redirect 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 import timezone
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 submissions.models import Submission
from .constants import PROSPECTIVE_FELLOW_INVITED,\
prospective_Fellow_statuses_dict,\
PROSPECTIVE_FELLOW_EVENT_EMAILED, PROSPECTIVE_FELLOW_EVENT_STATUSUPDATED,\
PROSPECTIVE_FELLOW_EVENT_COMMENT
from .forms import FellowshipForm, FellowshipTerminateForm, FellowshipRemoveSubmissionForm,\ from .forms import FellowshipForm, FellowshipTerminateForm, FellowshipRemoveSubmissionForm,\
FellowshipAddSubmissionForm, AddFellowshipForm, SubmissionAddFellowshipForm,\ FellowshipAddSubmissionForm, AddFellowshipForm, SubmissionAddFellowshipForm,\
FellowshipRemoveProceedingsForm, FellowshipAddProceedingsForm, SubmissionAddVotingFellowForm,\ FellowshipRemoveProceedingsForm, FellowshipAddProceedingsForm, SubmissionAddVotingFellowForm,\
FellowVotingRemoveSubmissionForm FellowVotingRemoveSubmissionForm,\
from .models import Fellowship ProspectiveFellowForm, ProspectiveFellowStatusForm, ProspectiveFellowEventForm
from .models import Fellowship, ProspectiveFellow, ProspectiveFellowEvent
from scipost.constants import SCIPOST_SUBJECT_AREAS
from scipost.mixins import PermissionsMixin
from mails.forms import EmailTemplateForm
from mails.views import MailView
@login_required @login_required
...@@ -292,3 +307,123 @@ def fellowship_add_proceedings(request, id): ...@@ -292,3 +307,123 @@ def fellowship_add_proceedings(request, id):
'form': form, 'form': form,
} }
return render(request, 'colleges/fellowship_proceedings_add.html', context) return render(request, 'colleges/fellowship_proceedings_add.html', context)
class ProspectiveFellowCreateView(PermissionsMixin, CreateView):
"""
Formview to create a new Prospective Fellow.
"""
permission_required = 'scipost.can_manage_college_composition'
form_class = ProspectiveFellowForm
template_name = 'colleges/prospectivefellow_form.html'
success_url = reverse_lazy('colleges:prospective_Fellows')
class ProspectiveFellowUpdateView(PermissionsMixin, UpdateView):
"""
Formview to update a Prospective Fellow.
"""
permission_required = 'scipost.can_manage_college_composition'
model = ProspectiveFellow
form_class = ProspectiveFellowForm
template_name = 'colleges/prospectivefellow_form.html'
success_url = reverse_lazy('colleges:prospective_Fellows')
class ProspectiveFellowUpdateStatusView(PermissionsMixin, UpdateView):
"""
Formview to update the status of a Prospective Fellow.
"""
permission_required = 'scipost.can_manage_college_composition'
model = ProspectiveFellow
fields = ['status']
success_url = reverse_lazy('colleges:prospective_Fellows')
def form_valid(self, form):
event = ProspectiveFellowEvent(
prosfellow=self.object,
event=PROSPECTIVE_FELLOW_EVENT_STATUSUPDATED,
comments=('Status updated to %s'
% prospective_Fellow_statuses_dict[form.cleaned_data['status']]),
noted_on=timezone.now(),
noted_by=self.request.user.contributor)
event.save()
return super().form_valid(form)
class ProspectiveFellowDeleteView(PermissionsMixin, DeleteView):
"""
Delete a Prospective Fellow.
"""
permission_required = 'scipost.can_manage_college_composition'
model = ProspectiveFellow
success_url = reverse_lazy('colleges:prospective_Fellows')
class ProspectiveFellowListView(PermissionsMixin, ListView):
"""
List the ProspectiveFellow object instances.
"""
permission_required = 'scipost.can_manage_college_composition'
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'].lower())
if 'expertise' in self.request.GET:
queryset = queryset.filter(expertises__contains=[self.request.GET['expertise']])
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['subject_areas'] = SCIPOST_SUBJECT_AREAS
context['pfstatus_form'] = ProspectiveFellowStatusForm()
context['pfevent_form'] = ProspectiveFellowEventForm()
return context
class ProspectiveFellowInitialEmailView(PermissionsMixin, MailView):
"""
Send a templated email to a Prospective Fellow.
"""
permission_required = 'scipost.can_manage_college_composition'
queryset = ProspectiveFellow.objects.all()
mail_code = 'prospectivefellows/invite_prospective_fellow_initial'
success_url = reverse_lazy('colleges:prospective_Fellows')
def form_valid(self, form):
"""
Create an event associated to this outgoing email.
"""
event = ProspectiveFellowEvent(
prosfellow=self.object,
event=PROSPECTIVE_FELLOW_EVENT_EMAILED,
comments='Emailed initial template',
noted_on=timezone.now(),
noted_by=self.request.user.contributor)
event.save()
self.object.status=PROSPECTIVE_FELLOW_INVITED
self.object.save()
return super().form_valid(form)
class ProspectiveFellowEventCreateView(PermissionsMixin, CreateView):
"""
Add an event for a Prospective Fellow.
"""
permission_required = 'scipost.can_manage_college_composition'
form_class = ProspectiveFellowEventForm
success_url = reverse_lazy('colleges:prospective_Fellows')
def form_valid(self, form):
form.instance.prosfellow = get_object_or_404(ProspectiveFellow, id=self.kwargs['pk'])
form.instance.noted_on = timezone.now()
form.instance.noted_by = self.request.user.contributor
messages.success(self.request, 'Event added successfully')
return super().form_valid(form)
...@@ -18,7 +18,8 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin): ...@@ -18,7 +18,8 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin):
self.pre_validation(*args, **kwargs) self.pre_validation(*args, **kwargs)
# This form shouldn't be is_bound==True is there is any non-relavant POST data given. # This form shouldn't be is_bound==True is there is any non-relavant POST data given.
data = args[0] or {} data = args[0] if args else {}
data = kwargs['data'] if 'data' in kwargs else data
if '%s-subject' % self.prefix in data.keys(): if '%s-subject' % self.prefix in data.keys():
data = { data = {
'%s-subject' % self.prefix: data.get('%s-subject' % self.prefix), '%s-subject' % self.prefix: data.get('%s-subject' % self.prefix),
...@@ -29,7 +30,7 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin): ...@@ -29,7 +30,7 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin):
data = None data = None
super().__init__(data or None) super().__init__(data or None)
if not self.original_recipient: if self.original_recipient == '':
self.fields['extra_recipient'].label = "Send this email to" self.fields['extra_recipient'].label = "Send this email to"
self.fields['extra_recipient'].required = True self.fields['extra_recipient'].required = True
...@@ -49,7 +50,7 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin): ...@@ -49,7 +50,7 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin):
self.bcc_list.append(self.cleaned_data.get('extra_recipient')) self.bcc_list.append(self.cleaned_data.get('extra_recipient'))
elif self.cleaned_data.get('extra_recipient') and not self.original_recipient: elif self.cleaned_data.get('extra_recipient') and not self.original_recipient:
self.original_recipient = [self.cleaned_data.get('extra_recipient')] self.original_recipient = [self.cleaned_data.get('extra_recipient')]
elif not self.original_recipient: elif self.original_recipient == '':
self.add_error('extra_recipient', 'Please fill the bcc field to send the mail.') self.add_error('extra_recipient', 'Please fill the bcc field to send the mail.')
self.validate_recipients() self.validate_recipients()
...@@ -60,6 +61,10 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin): ...@@ -60,6 +61,10 @@ class EmailTemplateForm(forms.Form, MailUtilsMixin):
self.save_data() self.save_data()
return data return data
def save(self):
self.send()
return self.instance
class HiddenDataForm(forms.Form): class HiddenDataForm(forms.Form):
def __init__(self, form, *args, **kwargs): def __init__(self, form, *args, **kwargs):
......
...@@ -4,6 +4,7 @@ __license__ = "AGPL v3" ...@@ -4,6 +4,7 @@ __license__ = "AGPL v3"
from django.contrib import messages from django.contrib import messages
from django.shortcuts import render from django.shortcuts import render
from django.views.generic.edit import UpdateView
from .forms import EmailTemplateForm, HiddenDataForm from .forms import EmailTemplateForm, HiddenDataForm
...@@ -113,3 +114,18 @@ class MailEditorMixin: ...@@ -113,3 +114,18 @@ class MailEditorMixin:
raise AttributeError('Did you check the order in which MailEditorMixin is used?') raise AttributeError('Did you check the order in which MailEditorMixin is used?')
messages.success(self.request, 'Mail sent') messages.success(self.request, 'Mail sent')
return response return response
class MailView(UpdateView):
template_name = 'mails/mail_form.html'
form_class = EmailTemplateForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['mail_code'] = self.mail_code
return kwargs
def form_valid(self, form):
response = super().form_valid(form)
form.send()
return response
<p>Dear {{ prosfel.get_title_display }} {{ prosfel.last_name }},</p>
<p>On behalf of the SciPost Foundation and in view of your professional expertise and reputation, I hereby would like to invite you to join our Editorial College and become one of our Editorial Fellows. By joining, you would be greatly helping us in our mission to establish a more healthy infrastructure for scientific publishing.</p>
<p>Please note that only well-known and respected senior academics are being contacted for this purpose. Academic reputation and involvement in the community are the most important criteria guiding our considerations of who should belong to the Editorial College. The current list of Fellows can be found at <a href="{% url 'scipost:about' %}">scipost.org/about</a>; on this page, you will also find basic information on SciPost and its guiding principles (you can also take a look at our <a href="{% url 'scipost:FAQ' %}">frequently asked questions page</a>). Functioning of the College proceeds according to the by-laws set out at <a href="{% url 'scipost:EdCol_by-laws' %}">scipost.org/EdCol_by-laws</a>. A short summary of the editorial workflow can be found at <a href="{% url 'submissions:editorial_workflow' %}">this page</a>.
<p>We do not pose any conditions on your involvement, and you would always remain in complete control of your level of commitment (devoting even a couple of hours per month would be enough to help out significantly).</p>
<p>I would be very happy to provide you with more information should you require it. Could I beg you to give us a response (by replying to this email) within the next couple of weeks?</p>
<p>Many thanks in advance,</p>
<p>Prof. J.-S. Caux, on behalf of the SciPost Foundation</p>
{% include 'email/_footer.html' %}
{
"subject": "Invitation to become a Fellow at SciPost",
"to_address": "email",
"bcc_to": "admin@scipost.org",
"from_address_name": "SciPost Admin",
"from_address": "admin@scipost.org",
"context_object": "prosfel"
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment