Add support for displaying and editing author affiliation and country

when submitting a draft, replace the Submission.authors line-based
text field with a JSON field
 - Legacy-Id: 12745
This commit is contained in:
Ole Laursen 2017-01-27 16:10:31 +00:00
parent 4426e3386f
commit 90051a1575
13 changed files with 110 additions and 93 deletions

View file

@ -177,7 +177,7 @@ class Recipient(models.Model):
addrs = []
if 'submission' in kwargs:
submission = kwargs['submission']
addrs.extend(["%s <%s>" % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]])
addrs.extend(["%s <%s>" % (author["name"], author["email"]) for author in submission.authors if author.get("email")])
return addrs
def gather_submission_group_chairs(self, **kwargs):
@ -200,7 +200,7 @@ class Recipient(models.Model):
doc=submission.existing_document()
if doc:
old_authors = [author.formatted_email() for author in doc.documentauthor_set.all() if author.email]
new_authors = [u'"%s" <%s>' % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]]
new_authors = [u'"%s" <%s>' % (author["name"], author["email"]) for author in submission.authors if author.get("email")]
addrs.extend(old_authors)
if doc.group and set(old_authors)!=set(new_authors):
if doc.group.type_id in ['wg','rg','ag']:
@ -212,7 +212,7 @@ class Recipient(models.Model):
if doc.stream_id and doc.stream_id not in ['ietf']:
addrs.extend(Recipient.objects.get(slug='stream_managers').gather(**{'streams':[doc.stream_id]}))
else:
addrs.extend([u"%s <%s>" % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]])
addrs.extend([u"%s <%s>" % (author["name"], author["email"]) for author in submission.authors if author.get("email")])
if submission.submitter_parsed()["email"]:
addrs.append(submission.submitter)
return addrs

View file

@ -574,8 +574,6 @@ def authors(request, id):
return redirect('drafts_view', id=id)
print form.is_valid(), form.errors
if form.is_valid():
person = form.cleaned_data['person']
email = form.cleaned_data['email']

View file

@ -12,6 +12,8 @@ from django.conf import settings
from django.utils.html import mark_safe
from django.core.urlresolvers import reverse as urlreverse
from django_countries.fields import countries
import debug # pyflakes:ignore
from ietf.doc.models import Document
@ -30,6 +32,14 @@ from ietf.submit.parsers.ps_parser import PSParser
from ietf.submit.parsers.xml_parser import XMLParser
from ietf.utils.draft import Draft
def clean_country(country):
country = country.upper()
for code, name in countries:
if country == code:
return code
if country == name.upper():
return code
return "" # unknown
class SubmissionUploadForm(forms.Form):
txt = forms.FileField(label=u'.txt format', required=False)
@ -178,18 +188,14 @@ class SubmissionUploadForm(forms.Form):
self.abstract = self.xmlroot.findtext('front/abstract').strip()
if type(self.abstract) is unicode:
self.abstract = unidecode(self.abstract)
self.author_list = []
author_info = self.xmlroot.findall('front/author')
for author in author_info:
author_dict = dict(
company = author.findtext('organization'),
last_name = author.attrib.get('surname'),
full_name = author.attrib.get('fullname'),
email = author.findtext('address/email'),
)
self.author_list.append(author_dict)
line = "%(full_name)s <%(email)s>" % author_dict
self.authors.append(line)
self.authors.append({
"name": author.attrib.get('fullname'),
"email": author.findtext('address/email'),
"affiliation": author.findtext('organization'),
"country": clean_country(author.findtext('address/postal/country')),
})
except forms.ValidationError:
raise
except Exception as e:
@ -325,18 +331,12 @@ class SubmissionUploadForm(forms.Form):
return None
class NameEmailForm(forms.Form):
"""For validating supplied submitter and author information."""
name = forms.CharField(required=True)
email = forms.EmailField(label=u'Email address')
#Fields for secretariat only
approvals_received = forms.BooleanField(label=u'Approvals received', required=False, initial=False)
email = forms.EmailField(label=u'Email address', required=True)
def __init__(self, *args, **kwargs):
email_required = kwargs.pop("email_required", True)
super(NameEmailForm, self).__init__(*args, **kwargs)
self.fields["email"].required = email_required
self.fields["name"].widget.attrs["class"] = "name"
self.fields["email"].widget.attrs["class"] = "email"
@ -346,6 +346,18 @@ class NameEmailForm(forms.Form):
def clean_email(self):
return self.cleaned_data["email"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
class AuthorForm(NameEmailForm):
affiliation = forms.CharField(max_length=100, required=False)
country = forms.ChoiceField(choices=[('', "(Not specified)")] + list(countries), required=False)
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
self.fields["email"].required = False
class SubmitterForm(NameEmailForm):
#Fields for secretariat only
approvals_received = forms.BooleanField(label=u'Approvals received', required=False, initial=False)
def cleaned_line(self):
line = self.cleaned_data["name"]
email = self.cleaned_data.get("email")

View file

@ -39,7 +39,7 @@ class Submission(models.Model):
words = models.IntegerField(null=True, blank=True)
formal_languages = models.ManyToManyField(FormalLanguageName, blank=True, help_text="Formal languages used in document")
authors = models.TextField(blank=True, help_text="List of author names and emails, one author per line, e.g. \"John Doe &lt;john@example.org&gt;\".")
authors = jsonfield.JSONField(default=list, help_text="List of authors with name, email, affiliation and country code.")
note = models.TextField(blank=True)
replaces = models.CharField(max_length=1000, blank=True)
@ -56,21 +56,6 @@ class Submission(models.Model):
def __unicode__(self):
return u"%s-%s" % (self.name, self.rev)
def authors_parsed(self):
if not hasattr(self, '_cached_authors_parsed'):
from ietf.submit.utils import ensure_person_email_info_exists
res = []
for line in self.authors.replace("\r", "").split("\n"):
line = line.strip()
if line:
parsed = parse_email_line(line)
if not parsed["email"]:
person, email = ensure_person_email_info_exists(**parsed)
parsed["email"] = email.address
res.append(parsed)
self._cached_authors_parsed = res
return self._cached_authors_parsed
def submitter_parsed(self):
return parse_email_line(self.submitter)

View file

@ -125,7 +125,7 @@ class SubmitTests(TestCase):
q = PyQuery(r.content)
print(q('div.has-error div.alert').text())
self.assertEqual(r.status_code, 302)
self.assertNoFormPostErrors(r, ".has-error,.alert-danger")
status_url = r["Location"]
for format in formats:
@ -133,10 +133,12 @@ class SubmitTests(TestCase):
self.assertEqual(Submission.objects.filter(name=name).count(), 1)
submission = Submission.objects.get(name=name)
self.assertTrue(all([ c.passed!=False for c in submission.checks.all() ]))
self.assertEqual(len(submission.authors_parsed()), 1)
author = submission.authors_parsed()[0]
self.assertEqual(len(submission.authors), 1)
author = submission.authors[0]
self.assertEqual(author["name"], "Author Name")
self.assertEqual(author["email"], "author@example.com")
self.assertEqual(author["affiliation"], "Test Centre Inc.")
# FIXMEself.assertEqual(author["country"], "UK")
return status_url
@ -664,7 +666,7 @@ class SubmitTests(TestCase):
"authors-prefix": ["authors-", "authors-0", "authors-1", "authors-2"],
})
self.assertEqual(r.status_code, 302)
self.assertNoFormPostErrors(r, ".has-error,.alert-danger")
submission = Submission.objects.get(name=name)
self.assertEqual(submission.title, "some title")
@ -676,14 +678,14 @@ class SubmitTests(TestCase):
self.assertEqual(submission.replaces, draft.docalias_set.all().first().name)
self.assertEqual(submission.state_id, "manual")
authors = submission.authors_parsed()
authors = submission.authors
self.assertEqual(len(authors), 3)
self.assertEqual(authors[0]["name"], "Person 1")
self.assertEqual(authors[0]["email"], "person1@example.com")
self.assertEqual(authors[1]["name"], "Person 2")
self.assertEqual(authors[1]["email"], "person2@example.com")
self.assertEqual(authors[2]["name"], "Person 3")
self.assertEqual(authors[2]["email"], "unknown-email-Person-3")
self.assertEqual(authors[2]["email"], "")
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Manual Post Requested" in outbox[-1]["Subject"])
@ -939,7 +941,6 @@ class SubmitTests(TestCase):
files = {"txt": submission_file(name, rev, group, "txt", "test_submission.nonascii", author=author) }
r = self.client.post(url, files)
self.assertEqual(r.status_code, 302)
status_url = r["Location"]
r = self.client.get(status_url)
@ -1443,8 +1444,8 @@ Subject: test
self.assertEqual(Submission.objects.filter(name=name).count(), 1)
submission = Submission.objects.get(name=name)
self.assertTrue(all([ c.passed!=False for c in submission.checks.all() ]))
self.assertEqual(len(submission.authors_parsed()), 1)
author = submission.authors_parsed()[0]
self.assertEqual(len(submission.authors), 1)
author = submission.authors[0]
self.assertEqual(author["name"], "Author Name")
self.assertEqual(author["email"], "author@example.com")

View file

@ -42,7 +42,7 @@ def validate_submission(submission):
if not submission.abstract:
errors['abstract'] = 'Abstract is empty or was not found'
if not submission.authors_parsed():
if not submission.authors:
errors['authors'] = 'No authors found'
# revision
@ -427,14 +427,16 @@ def ensure_person_email_info_exists(name, email):
def update_authors(draft, submission):
persons = []
for order, author in enumerate(submission.authors_parsed()):
person, email = ensure_person_email_info_exists(author["name"], author["email"])
for order, author in enumerate(submission.authors):
person, email = ensure_person_email_info_exists(author["name"], author.get("email"))
a = DocumentAuthor.objects.filter(document=draft, person=person).first()
if not a:
a = DocumentAuthor(document=draft, person=person)
a.email = email
a.affiliation = author.get("affiliation") or ""
a.country = author.get("country") or ""
a.order = order
a.save()

View file

@ -21,7 +21,7 @@ from ietf.ietfauth.utils import has_role, role_required
from ietf.mailtrigger.utils import gather_address_lists
from ietf.message.models import Message, MessageAttachment
from ietf.name.models import FormalLanguageName
from ietf.submit.forms import ( SubmissionUploadForm, NameEmailForm, EditSubmissionForm,
from ietf.submit.forms import ( SubmissionUploadForm, AuthorForm, SubmitterForm, EditSubmissionForm,
PreapprovalForm, ReplacesForm, SubmissionEmailForm, MessageModelForm )
from ietf.submit.mail import ( send_full_url, send_approval_request_to_group,
send_submission_confirmation, send_manual_post_request, add_submission_email, get_reply_to )
@ -83,8 +83,7 @@ def upload_submission(request):
for author in form.parsed_draft.get_author_list():
full_name, first_name, middle_initial, last_name, name_suffix, email, company = author
line = full_name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
email = (email or "").strip()
name = full_name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
if email:
try:
@ -92,29 +91,31 @@ def upload_submission(request):
except ValidationError:
email = ""
if email:
# Try various ways of handling name and email, in order to avoid
# triggering a 500 error here. If the document contains non-ascii
# characters, it will be flagged later by the idnits check.
try:
line += u" <%s>" % email
except UnicodeDecodeError:
def turn_into_unicode(s):
if s is None:
return u""
if isinstance(s, unicode):
return s
else:
try:
line = line.decode('utf-8')
email = email.decode('utf-8')
line += u" <%s>" % email
return s.decode("utf-8")
except UnicodeDecodeError:
try:
line = line.decode('latin-1')
email = email.decode('latin-1')
line += u" <%s>" % email
return s.decode("latin-1")
except UnicodeDecodeError:
try:
line += " <%s>" % email
except UnicodeDecodeError:
pass
return ""
authors.append(line)
name = turn_into_unicode(name)
email = turn_into_unicode(email)
company = turn_into_unicode(company)
authors.append({
"name": name,
"email": email,
"affiliation": company,
# FIXME: missing country
})
if form.abstract:
abstract = form.abstract
@ -143,7 +144,7 @@ def upload_submission(request):
submission.abstract = abstract
submission.pages = form.parsed_draft.get_pagecount()
submission.words = form.parsed_draft.get_wordcount()
submission.authors = "\n".join(authors)
submission.authors = authors
submission.first_two_pages = ''.join(form.parsed_draft.pages[:2])
submission.file_size = file_size
submission.file_types = ','.join(form.file_types)
@ -260,7 +261,7 @@ def submission_status(request, submission_id, access_token=None):
doc = submission.existing_document()
if doc and doc.group:
old_authors = [ author.person for author in doc.documentauthor_set.all() ]
new_authors = [ get_person_from_name_email(**p) for p in submission.authors_parsed() ]
new_authors = [ get_person_from_name_email(author["name"], author.get("email")) for author in submission.authors ]
group_authors_changed = set(old_authors)!=set(new_authors)
message = None
@ -275,7 +276,7 @@ def submission_status(request, submission_id, access_token=None):
message = ('success', 'The submission is pending approval by the authors of the previous version. An email has been sent to: %s' % ", ".join(confirmation_list))
submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
submitter_form = SubmitterForm(initial=submission.submitter_parsed(), prefix="submitter")
replaces_form = ReplacesForm(name=submission.name,initial=DocAlias.objects.filter(name__in=submission.replaces.split(",")))
if request.method == 'POST':
@ -284,7 +285,7 @@ def submission_status(request, submission_id, access_token=None):
if not can_edit:
return HttpResponseForbidden("You do not have permission to perform this action")
submitter_form = NameEmailForm(request.POST, prefix="submitter")
submitter_form = SubmitterForm(request.POST, prefix="submitter")
replaces_form = ReplacesForm(request.POST, name=submission.name)
validations = [submitter_form.is_valid(), replaces_form.is_valid()]
if all(validations):
@ -432,7 +433,7 @@ def edit_submission(request, submission_id, access_token=None):
# submission itself, one for the submitter, and a list of forms
# for the authors
empty_author_form = NameEmailForm(email_required=False)
empty_author_form = AuthorForm()
if request.method == 'POST':
# get a backup submission now, the model form may change some
@ -440,9 +441,9 @@ def edit_submission(request, submission_id, access_token=None):
prev_submission = Submission.objects.get(pk=submission.pk)
edit_form = EditSubmissionForm(request.POST, instance=submission, prefix="edit")
submitter_form = NameEmailForm(request.POST, prefix="submitter")
submitter_form = SubmitterForm(request.POST, prefix="submitter")
replaces_form = ReplacesForm(request.POST,name=submission.name)
author_forms = [ NameEmailForm(request.POST, email_required=False, prefix=prefix)
author_forms = [ AuthorForm(request.POST, prefix=prefix)
for prefix in request.POST.getlist("authors-prefix")
if prefix != "authors-" ]
@ -454,9 +455,9 @@ def edit_submission(request, submission_id, access_token=None):
submission.submitter = submitter_form.cleaned_line()
replaces = replaces_form.cleaned_data.get("replaces", [])
submission.replaces = ",".join(o.name for o in replaces)
submission.authors = "\n".join(f.cleaned_line() for f in author_forms)
if hasattr(submission, '_cached_authors_parsed'):
del submission._cached_authors_parsed
submission.authors = [ { attr: f.cleaned_data.get(attr) or ""
for attr in ["name", "email", "affiliation", "country"] }
for f in author_forms ]
edit_form.save(commit=False) # transfer changes
if submission.rev != prev_submission.rev:
@ -491,10 +492,10 @@ def edit_submission(request, submission_id, access_token=None):
form_errors = True
else:
edit_form = EditSubmissionForm(instance=submission, prefix="edit")
submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
submitter_form = SubmitterForm(initial=submission.submitter_parsed(), prefix="submitter")
replaces_form = ReplacesForm(name=submission.name,initial=DocAlias.objects.filter(name__in=submission.replaces.split(",")))
author_forms = [ NameEmailForm(initial=author, email_required=False, prefix="authors-%s" % i)
for i, author in enumerate(submission.authors_parsed()) ]
author_forms = [ AuthorForm(initial=author, prefix="authors-%s" % i)
for i, author in enumerate(submission.authors) ]
return render(request, 'submit/edit_submission.html',
{'selected': 'status',

View file

@ -3,7 +3,7 @@ A New Internet-Draft is available from the on-line Internet-Drafts directories.
{% if submission.group %}This draft is a work item of the {{ submission.group.name }}{% if group.type.name %} {{ group.type.name }}{% endif %} of the {% if group.type_id == "rg" %}IRTF{% else %}IETF{% endif %}.{% endif %}
Title : {{ submission.title }}
Author{{ submission.authors_parsed|pluralize:" ,s" }} : {% for author in submission.authors_parsed %}{{ author.name }}{% if not forloop.last %}
Author{{ submission.authors|pluralize:" ,s" }} : {% for author in submission.authors %}{{ author.name }}{% if not forloop.last %}
{% endif %}{% endfor %}
Filename : {{ submission.name }}-{{ submission.rev }}.txt
Pages : {{ submission.pages }}

View file

@ -22,7 +22,7 @@ To approve the draft, go to this URL (note: you need to login to be able to appr
Authors:
{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
{% for author in submission.authors %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
{% endfor %}
{% endautoescape %}

View file

@ -22,7 +22,7 @@ I-D Submission Tool URL:
Authors:
{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
{% for author in submission.authors %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
{% endfor %}
Comment to the secretariat:

View file

@ -2,7 +2,7 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load staticfiles %}
{% load ietf_filters submit_tags %}
{% load ietf_filters submit_tags country %}
{% block title %}Submission status of {{ submission.name }}-{{ submission.rev }}{% endblock %}
@ -195,17 +195,21 @@
<tr>
<th>Authors</th>
<td>
{% with submission.authors_parsed as authors %}
{% with submission.authors as authors %}
{{ authors|length }} author{{ authors|pluralize }}
{% endwith %}
{% if errors.authors %}<p class="text-danger"><b>{{ errors.authors|safe }}</b></p>{% endif %}
</td>
</tr>
{% for author in submission.authors_parsed %}
{% for author in submission.authors %}
<tr>
<th>Author {{ forloop.counter }}</th>
<td>{{ author.name }} {% if author.email %}&lt;{{ author.email }}&gt;{% endif %}</td>
<td>
{{ author.name }} {% if author.email %}&lt;{{ author.email }}&gt;{% endif %}
{% if author.affiliation %}- {{ author.affiliation }}{% endif %}
{% if author.country %}- {{ author.country|country_name }}{% endif %}
</td>
</tr>
{% endfor %}

View file

@ -11,8 +11,8 @@
{% load ietf_filters %}
{% buttons %}
{% for author in submission.authors_parsed %}
<input type="button" class="author btn btn-default" data-name="{{ author.name }}" data-email="{{ author.email }}" value="{{ author.name }}">
{% for author in submission.authors %}
<input type="button" class="author btn btn-default" data-name="{{ author.name }}" data-email="{% if author.email %}{{ author.email }}{% endif %}" value="{{ author.name }}">
{% endfor %}
{% endbuttons %}

View file

@ -0,0 +1,14 @@
from django.template.base import Library
from django.template.defaultfilters import stringfilter
from django_countries import countries
register = Library()
@register.filter(is_safe=True)
@stringfilter
def country_name(value):
"""
Converts country code to country name
"""
return dict(countries).get(value, "")