SciPost Code Repository

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

Improve partners

parent 4f4c6903
No related branches found
No related tags found
No related merge requests found
Showing
with 291 additions and 29 deletions
...@@ -18,14 +18,24 @@ PARTNER_KINDS = ( ...@@ -18,14 +18,24 @@ PARTNER_KINDS = (
PROSPECTIVE_PARTNER_REQUESTED = 'requested' PROSPECTIVE_PARTNER_REQUESTED = 'requested'
PROSPECTIVE_PARTNER_ADDED = 'added' PROSPECTIVE_PARTNER_ADDED = 'added'
PROSPECTIVE_PARTNER_APPROACHED = 'approached'
PROSPECTIVE_PARTNER_PROCESSED = 'processed'
PROSPECTIVE_PARTNER_STATUS = ( PROSPECTIVE_PARTNER_STATUS = (
(PROSPECTIVE_PARTNER_REQUESTED, 'Requested (from online form)'), (PROSPECTIVE_PARTNER_REQUESTED, 'Requested (from online form)'),
(PROSPECTIVE_PARTNER_ADDED, 'Added internally'), (PROSPECTIVE_PARTNER_ADDED, 'Added internally'),
('processed', 'Processed into Partner object'), (PROSPECTIVE_PARTNER_APPROACHED, 'Approached'),
(PROSPECTIVE_PARTNER_PROCESSED, 'Processed into Partner'),
) )
PROSPECTIVE_PARTNER_EVENT_REQUESTED = 'requested'
PROSPECTIVE_PARTNER_EVENT_COMMENT = 'comment',
PROSPECTIVE_PARTNER_EVENT_EMAIL_SENT = 'email_sent'
PROSPECTIVE_PARTNER_EVENT_PROMOTED = 'promoted'
PROSPECTIVE_PARTNER_EVENTS = ( PROSPECTIVE_PARTNER_EVENTS = (
('comment', 'Comment added'), (PROSPECTIVE_PARTNER_EVENT_REQUESTED, 'Requested (from online form)'),
(PROSPECTIVE_PARTNER_EVENT_COMMENT, 'Comment added'),
(PROSPECTIVE_PARTNER_EVENT_EMAIL_SENT, 'Email sent'),
(PROSPECTIVE_PARTNER_EVENT_PROMOTED, 'Promoted to Partner'),
) )
......
...@@ -39,6 +39,33 @@ class ProspectiveContactForm(forms.ModelForm): ...@@ -39,6 +39,33 @@ class ProspectiveContactForm(forms.ModelForm):
widgets = {'prospartner': forms.HiddenInput()} widgets = {'prospartner': forms.HiddenInput()}
class EmailProspectivePartnerContactForm(forms.Form):
email_subject = forms.CharField(widget=forms.Textarea(),
initial='SciPost Supporting Partners Board')
message = forms.CharField(widget=forms.Textarea(), required=False)
include_SPB_summary = forms.BooleanField(
required=False, initial=False,
label='include SPB summary with message')
def __init__(self, *args, **kwargs):
super(EmailProspectivePartnerContactForm, self).__init__(*args, **kwargs)
self.fields['email_subject'].widget.attrs.update(
{'rows': 1})
self.fields['message'].widget.attrs.update(
{'placeholder': 'Write your message in this box (optional).'})
# class ProspectivePartnerContactSelectForm(forms.Form):
# def __init__(self, *args, **kwargs):
# prospartner_id = kwargs.pop('prospartner.id')
# super(ProspectivePartnerContactSelectForm, self).__init(*args, **kwargs)
# self.fields['contact'] = forms.ModelChoiceField(
# queryset=ProspectiveContact.objects.filter(
# prospartner__pk=prospartner_id).order_by('last_name'),
# required=True)
class ProspectivePartnerEventForm(forms.ModelForm): class ProspectivePartnerEventForm(forms.ModelForm):
class Meta: class Meta:
model = ProspectivePartnerEvent model = ProspectivePartnerEvent
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2017-06-08 15:10
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('partners', '0008_auto_20170604_2228'),
]
operations = [
migrations.AlterField(
model_name='prospectivepartnerevent',
name='event',
field=models.CharField(choices=[('comment', 'Comment added'), ('email_sent', 'Email sent'), ('promoted', 'Promoted to Partner')], max_length=64),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2017-06-08 18:49
from __future__ import unicode_literals
from django.db import migrations, models
import scipost.models
class Migration(migrations.Migration):
dependencies = [
('partners', '0009_auto_20170608_1710'),
]
operations = [
migrations.AlterField(
model_name='prospectivepartner',
name='status',
field=models.CharField(choices=[('requested', 'Requested (from online form)'), ('added', 'Added internally'), ('approached', 'Approached'), ('processed', 'Processed into Partner')], default='added', max_length=32),
),
migrations.AlterField(
model_name='prospectivepartnerevent',
name='event',
field=models.CharField(choices=[('requested', 'Requested (from online form)'), (('comment',), 'Comment added'), ('email_sent', 'Email sent'), ('promoted', 'Promoted to Partner')], max_length=64),
),
migrations.AlterField(
model_name='prospectivepartnerevent',
name='noted_by',
field=models.ForeignKey(blank=True, null=True, on_delete=models.SET(scipost.models.get_sentinel_user), to='scipost.Contributor'),
),
]
...@@ -10,6 +10,7 @@ from .constants import PARTNER_KINDS, PARTNER_STATUS, CONSORTIUM_STATUS, MEMBERS ...@@ -10,6 +10,7 @@ from .constants import PARTNER_KINDS, PARTNER_STATUS, CONSORTIUM_STATUS, MEMBERS
from .managers import MembershipAgreementManager from .managers import MembershipAgreementManager
from scipost.constants import TITLE_CHOICES from scipost.constants import TITLE_CHOICES
from scipost.models import get_sentinel_user
######################## ########################
...@@ -48,13 +49,18 @@ class ProspectiveContact(models.Model): ...@@ -48,13 +49,18 @@ class ProspectiveContact(models.Model):
email = models.EmailField() email = models.EmailField()
role = models.CharField(max_length=128) role = models.CharField(max_length=128)
def __str__(self):
return "%s %s %s" % (self.get_title_display(), self.first_name, self.last_name)
class ProspectivePartnerEvent(models.Model): class ProspectivePartnerEvent(models.Model):
prospartner = models.ForeignKey('partners.ProspectivePartner', on_delete=models.CASCADE) prospartner = models.ForeignKey('partners.ProspectivePartner', on_delete=models.CASCADE)
event = models.CharField(max_length=64, choices=PROSPECTIVE_PARTNER_EVENTS) event = models.CharField(max_length=64, choices=PROSPECTIVE_PARTNER_EVENTS)
comments = models.TextField(blank=True) comments = models.TextField(blank=True)
noted_on = models.DateTimeField(auto_now_add=True) noted_on = models.DateTimeField(auto_now_add=True)
noted_by = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE) noted_by = models.ForeignKey('scipost.Contributor',
on_delete=models.SET(get_sentinel_user),
blank=True, null=True)
def __str__(self): def __str__(self):
return '%s: %s' % (str(self.prospective_partner), self.get_event_display()) return '%s: %s' % (str(self.prospective_partner), self.get_event_display())
......
<li id="{{ event.id }}"> <li id="{{ event.id }}">
<div class="font-weight-bold">{{ event.get_event_display }} <small class="text-muted">noted {{ event.noted_on }} by {{ event.noted_by }}</small> <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> </div>
{% if event.comments %} {% if event.comments %}
<div>{{ event.comments|linebreaks }}</div> <div>{{ event.comments|linebreaks }}</div>
......
...@@ -17,26 +17,28 @@ ...@@ -17,26 +17,28 @@
<div class="col-md-7"> <div class="col-md-7">
<h3>Contacts:</h3> <h3>Contacts:</h3>
<a class="d-inline-block mb-2" href="{% url 'partners:add_prospartner_contact' prospartner_id=pp.id %}">Add a contact</a> <a class="d-inline-block mb-2" href="{% url 'partners:add_prospartner_contact' prospartner_id=pp.id %}">Add a contact</a>
<table class="table"> <table class="table">
<thead> <thead>
<th>Role</th> <th>Role</th>
<th>Contact</th> <th>Name</th>
<th>Email</th> <th>Email</th>
</thead> <th>Actions</th>
<tbody> </thead>
{% for contact in pp.prospectivecontact_set.all %} <tbody>
<tr> {% for contact in pp.prospectivecontact_set.all %}
<td>{{ contact.role }}</td> <tr>
<td>{{ contact.get_title_display }} {{ contact.first_name }} {{ contact.last_name }}</td> <td>{{ contact.role }}</td>
<td>{{ contact.email }}</td> <td>{{ contact.get_title_display }} {{ contact.first_name }} {{ contact.last_name }}</td>
</tr> <td>{{ contact.email }}</td>
{% empty %} <td><a href="{% url 'partners:email_prospartner_contact' contact_id=contact.id %}">Compose email</a></td>
<tr> </tr>
<td colspan="3">No contacts found, <a href="{% url 'partners:add_prospartner_contact' prospartner_id=pp.id %}">add a contact</a>.</td> {% empty %}
</tr> <tr>
{% endfor %} <td colspan="3">No contacts found, <a href="{% url 'partners:add_prospartner_contact' prospartner_id=pp.id %}">add a contact</a>.</td>
</tbody> </tr>
</table> {% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
...@@ -53,7 +55,7 @@ ...@@ -53,7 +55,7 @@
</ul> </ul>
</div> </div>
<div class="col-5"> <div class="col-5">
<h3>Add an event</h3> <h3>Add an event for this Prospective Partner</h3>
<form action="{% url 'partners:add_prospartner_event' prospartner_id=pp.id %}" method="post"> <form action="{% url 'partners:add_prospartner_event' prospartner_id=pp.id %}" method="post">
{% csrf_token %} {% csrf_token %}
{{ ppevent_form|bootstrap }} {{ ppevent_form|bootstrap }}
......
{% extends 'scipost/base.html' %}
{% block pagetitle %}: Supporting Partners: email contact{% endblock pagetitle %}
{% load bootstrap %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="highlight">Email a Prospective Partner Contact</h1>
</div>
</div>
<div class="row">
<div class="col-12">
<form action="{% url 'partners:email_prospartner_contact' contact_id=contact.id %}" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input class="btn btn-primary" type="submit" value="Submit"/>
</form>
{% if errormessage %}
<p class="text-danger">{{ errormessage }}</p>
{% endif %}
</div>
</div>
{% endblock content %}
...@@ -8,8 +8,10 @@ urlpatterns = [ ...@@ -8,8 +8,10 @@ urlpatterns = [
url(r'^manage$', views.manage, name='manage'), url(r'^manage$', views.manage, name='manage'),
url(r'^add_prospective_partner$', views.add_prospective_partner, url(r'^add_prospective_partner$', views.add_prospective_partner,
name='add_prospective_partner'), name='add_prospective_partner'),
url(r'^add_prospective_contact/(?P<prospartner_id>[0-9]+)$', url(r'^add_prospartner_contact/(?P<prospartner_id>[0-9]+)$',
views.add_prospartner_contact, name='add_prospartner_contact'), views.add_prospartner_contact, name='add_prospartner_contact'),
url(r'^email_prospartner_contact/(?P<contact_id>[0-9]+)$',
views.email_prospartner_contact, name='email_prospartner_contact'),
url(r'^add_prospartner_event/(?P<prospartner_id>[0-9]+)$', url(r'^add_prospartner_event/(?P<prospartner_id>[0-9]+)$',
views.add_prospartner_event, name='add_prospartner_event'), views.add_prospartner_event, name='add_prospartner_event'),
] ]
from common.utils import BaseMailUtil
class PartnerUtils(BaseMailUtil):
mail_sender = 'partners@scipost.org'
mail_sender_title = 'SciPost Supporting Partners'
@classmethod
def email_prospartner_contact(cls):
"""
Email a contact of a ProspectivePartner,
for example to establish a first contact
and invite participation to the Supporting Partners Board.
"""
cls._send_mail(cls, 'email_prospartner_contact',
[cls._context['contact'].email,],
cls._context['email_subject'])
...@@ -5,11 +5,17 @@ from django.utils import timezone ...@@ -5,11 +5,17 @@ from django.utils import timezone
from guardian.decorators import permission_required from guardian.decorators import permission_required
from .constants import PROSPECTIVE_PARTNER_REQUESTED from .constants import PROSPECTIVE_PARTNER_REQUESTED, PROSPECTIVE_PARTNER_ADDED,\
from .models import Partner, ProspectivePartner, ProspectiveContact, MembershipAgreement PROSPECTIVE_PARTNER_APPROACHED, PROSPECTIVE_PARTNER_ADDED,\
PROSPECTIVE_PARTNER_EVENT_REQUESTED, PROSPECTIVE_PARTNER_EVENT_EMAIL_SENT
from .models import Partner, ProspectivePartner, ProspectiveContact,\
ProspectivePartnerEvent, MembershipAgreement
from .forms import ProspectivePartnerForm, ProspectiveContactForm,\ from .forms import ProspectivePartnerForm, ProspectiveContactForm,\
ProspectivePartnerEventForm, MembershipQueryForm EmailProspectivePartnerContactForm,\
ProspectivePartnerEventForm, MembershipQueryForm
from common.utils import BaseMailUtil
from .utils import PartnerUtils
def supporting_partners(request): def supporting_partners(request):
context = {} context = {}
...@@ -40,6 +46,10 @@ def membership_request(request): ...@@ -40,6 +46,10 @@ def membership_request(request):
email=query_form.cleaned_data['email'], email=query_form.cleaned_data['email'],
) )
contact.save() contact.save()
prospartnerevent = ProspectivePartnerEvent(
prospartner = prospartner,
event = PROSPECTIVE_PARTNER_EVENT_REQUESTED,)
prospartnerevent.save()
ack_message = ('Thank you for your SPB Membership query. ' ack_message = ('Thank you for your SPB Membership query. '
'We will get back to you in the very near future ' 'We will get back to you in the very near future '
'with further details.') 'with further details.')
...@@ -89,6 +99,37 @@ def add_prospartner_contact(request, prospartner_id): ...@@ -89,6 +99,37 @@ def add_prospartner_contact(request, prospartner_id):
return render(request, 'partners/add_prospartner_contact.html', context) return render(request, 'partners/add_prospartner_contact.html', context)
@permission_required('scipost.can_manage_SPB', return_403=True)
@transaction.atomic
def email_prospartner_contact(request, contact_id):
contact = get_object_or_404(ProspectiveContact, pk=contact_id)
form = EmailProspectivePartnerContactForm(request.POST or None)
if form.is_valid():
comments = 'Email sent to %s.' % str(contact)
prospartnerevent = ProspectivePartnerEvent(
prospartner = contact.prospartner,
event = PROSPECTIVE_PARTNER_EVENT_EMAIL_SENT,
comments = comments,
noted_on = timezone.now(),
noted_by = request.user.contributor)
prospartnerevent.save()
if contact.prospartner.status in [PROSPECTIVE_PARTNER_REQUESTED,
PROSPECTIVE_PARTNER_ADDED]:
contact.prospartner.status = PROSPECTIVE_PARTNER_APPROACHED
contact.prospartner.save()
PartnerUtils.load({'contact': contact,
'email_subject': form.cleaned_data['email_subject'],
'message': form.cleaned_data['message'],
'include_SPB_summary': form.cleaned_data['include_SPB_summary']})
PartnerUtils.email_prospartner_contact()
messages.success(request, 'Email successfully sent')
return redirect(reverse('partners:manage'))
context = {'contact': contact, 'form': form}
return render(request, 'partners/email_prospartner_contact.html', context)
@permission_required('scipost.can_manage_SPB', return_403=True) @permission_required('scipost.can_manage_SPB', return_403=True)
@transaction.atomic @transaction.atomic
def add_prospartner_event(request, prospartner_id): def add_prospartner_event(request, prospartner_id):
......
No preview for this file type
Dear {{ contact.get_title_display }} {{ contact.last_name }}, \n\n
{% if message %}
{{ message }}
{% endif %}
{% if include_SPB_summary %}
You might by now have heard of SciPost, a recently-launched initiative aiming to bring disruptive change to current academic publishing practices.
\n\nIn summary, SciPost.org is a publication portal managed by professional scientists, offering (among others) high-quality Open Access journals with innovative forms of refereeing, and a means of commenting on all existing literature. SciPost is established as a not-for-profit foundation devoted to serving the interests of the international scientific community.
\n\nThe site is anchored at https://scipost.org. Many further details about SciPost, its principles, ideals and implementation can be found at https://scipost.org/about and https://scipost.org/FAQ.
\n\nCrucially, as explained on our Partners page at https://scipost.org/partners, SciPost follows a completely different funding model than traditional publishers, and aims at providing a disruptive cost-slashing alternative to existing practises. SciPost charges neither subscription fees, nor article processing charges; its activities are instead to be financed through a Supporting Partners Board, formed by a worldwide collection of institutions and organizations which directly or indirectly benefit from SciPost’s activities.
\n\nSupport takes the form of a small financial commitment, collectively pooled to enable SciPost to perform all its publication-related activities, maintain its online portal and implement its long-term development plan.
\n\nIn the agreement template, which you can find online at https://scipost.org/static/scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf, you will find many more specific details about our operations, requirements and funding strategy. I would greatly appreciate if you took a few minutes to read through this document.
\n\nIt would be a privilege to welcome you as members of our Supporting Partners Board, and are hereby contacting you to enquire whether your institution would consider joining. Your support at this time is crucially required to make our initiative viable, and to help make it possible for the community to reap all the benefits deriving form its viable implementation.
\n\nI will be happy to provide any required further details. In the meantime, I send you my best wishes and sincerely hope that SciPost will be able to count on your support.
\n\nOn behalf of the SciPost Foundation,
\n\nProf. dr Jean-Sébastien Caux
\n---------------------------------------------
\nInstitute for Theoretial Physics\nUniversity of Amsterdam
\nScience Park 904\n1098 XH Amsterdam\nThe Netherlands
\n---------------------------------------------
\ntel.: +31 (0)20 5255775\nfax: +31 (0)20 5255778
\n---------------------------------------------
{% endif %}
<p>Dear {{ contact.get_title_display }} {{ contact.last_name }},</p>
{% if message %}
<p>
{{ message|linebreaks }}
</p>
{% endif %}
{% if include_SPB_summary %}
<p>
You might by now have heard of SciPost, a recently-launched initiative aiming to bring disruptive change to current academic publishing practices.
</p>
<p>
In summary, SciPost.org is a publication portal managed by professional scientists, offering (among others) high-quality Open Access journals with innovative forms of refereeing, and a means of commenting on all existing literature. SciPost is established as a not-for-profit foundation devoted to serving the interests of the international scientific community.
</p>
<p>
The site is anchored at <a href="https://scipost.org">SciPost.org</a>. Many further details about SciPost, its principles, ideals and implementation can be found on the <a href="https://scipost.org/about">about</a> and <a href="https://scipost.org/FAQ">FAQ</a> pages.
</p>
<p>
Crucially, as explained on our <a href="https://scipost.org/partners">Partners page</a>, SciPost follows a completely different funding model than traditional publishers, and aims at providing a disruptive cost-slashing alternative to existing practises. SciPost charges neither subscription fees, nor article processing charges; its activities are instead to be financed through a Supporting Partners Board, formed by a worldwide collection of institutions and organizations which directly or indirectly benefit from SciPost’s activities.
</p>
<p>
Support takes the form of a small financial commitment, collectively pooled to enable SciPost to perform all its publication-related activities, maintain its online portal and implement its long-term development plan.
</p>
<p>
In the <a href="https://scipost.org/static/scipost/SPB/SciPost_Supporting_Partner_Agreement.pdf">agreement template</a>, you will find many more specific details about our operations, requirements and funding strategy. I would greatly appreciate if you took a few minutes to read through this document.
</p>
<p>
It would be a privilege to welcome you as members of our Supporting Partners Board, and are hereby contacting you to enquire whether your institution would consider joining. Your support at this time is crucially required to make our initiative viable, and to help make it possible for the community to reap all the benefits deriving form its viable implementation.
</p>
<p>
I will be happy to provide any required further details. In the meantime, I send you my best wishes and sincerely hope that SciPost will be able to count on your support.
</p>
<p>On behalf of the SciPost Foundation,</p>
<br/>Prof. dr Jean-Sébastien Caux
<br/>---------------------------------------------
<br/>Institute for Theoretial Physics
<br/>University of Amsterdam
<br/>Science Park 904
<br/>1098 XH Amsterdam
<br/>The Netherlands
<br/>---------------------------------------------
<br/>tel.: +31 (0)20 5255775\nfax: +31 (0)20 5255778
<br/>---------------------------------------------
{% endif %}
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