Skip to content

Commit 7c79a4d

Browse files
committed
Edit of metadata and request manual post. See ietf-tools#595
- Legacy-Id: 2845
1 parent 21bccc8 commit 7c79a4d

9 files changed

Lines changed: 505 additions & 35 deletions

File tree

ietf/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@
189189
LIAISON_ATTACH_URL = '/documents/LIAISON/'
190190

191191
# ID Submission Tool settings
192+
IDST_FROM_EMAIL = 'IETF I-D Submission Tool <idsubmission@ietf.org>'
193+
IDST_TO_EMAIL = 'internet-drafts@ietf.org'
194+
192195
# Days from meeting to cut off dates on submit
193196
FIRST_CUTOFF_DAYS = 5
194197
SECOND_CUTOFF_DAYS = 3

ietf/submit/forms.py

Lines changed: 175 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1+
import sha
2+
import random
13
import os
24
import subprocess
35
import datetime
46

57
from django import forms
8+
from django.forms.fields import email_re
69
from django.conf import settings
710
from django.template.loader import render_to_string
811
from django.utils.html import mark_safe
912

1013
from ietf.idtracker.models import InternetDraft, IETFWG
1114
from ietf.proceedings.models import Meeting
1215
from ietf.submit.models import IdSubmissionDetail, TempIdAuthors
16+
from ietf.submit.utils import MANUAL_POST_REQUESTED, NONE_WG, UPLOADED, WAITING_AUTHENTICATION
1317
from ietf.submit.parsers.pdf_parser import PDFParser
1418
from ietf.submit.parsers.plain_parser import PlainParser
1519
from ietf.submit.parsers.ps_parser import PSParser
1620
from ietf.submit.parsers.xml_parser import XMLParser
21+
from ietf.utils.mail import send_mail
1722
from ietf.utils.draft import Draft
1823

1924

@@ -42,6 +47,7 @@ def __init__(self, *args, **kwargs):
4247
self.draft = None
4348
self.filesize = None
4449
self.group = None
50+
self.file_type = []
4551
self.read_dates()
4652

4753
def read_dates(self):
@@ -200,15 +206,16 @@ def get_draft(self):
200206
return self.draft
201207

202208
def save(self):
203-
for fd in [self.cleaned_data['txt'], self.cleaned_data['pdf'],
204-
self.cleaned_data['xml'], self.cleaned_data['ps']]:
209+
for ext in ['txt', 'pdf', 'xml', 'ps']:
210+
fd = self.cleaned_data[ext]
205211
if not fd:
206212
continue
207-
filename = os.path.join(self.staging_path, fd.name)
213+
self.file_type.append('.%s' % ext)
214+
filename = os.path.join(self.staging_path, '%s-%s.%s' % (self.draft.filename, self.draft.revision, ext))
208215
destination = open(filename, 'wb+')
209216
for chunk in fd.chunks():
210217
destination.write(chunk)
211-
destination.close()
218+
destination.close()
212219
self.check_idnits()
213220
return self.save_draft_info(self.draft)
214221

@@ -222,7 +229,7 @@ def get_working_group(self):
222229
existing_draft = InternetDraft.objects.filter(filename=filename)
223230
if existing_draft:
224231
group = existing_draft[0].group and existing_draft[0].group.ietfwg or None
225-
if group and group.pk != 1027:
232+
if group and group.pk != NONE_WG:
226233
return group
227234
else:
228235
return None
@@ -258,7 +265,9 @@ def save_draft_info(self, draft):
258265
group_acronym=self.group,
259266
remote_ip=self.remote_ip,
260267
first_two_pages=''.join(draft.pages[:2]),
261-
status_id=1, # Status 1 - upload
268+
status_id=UPLOADED,
269+
abstract=draft.get_abstract(),
270+
file_type=','.join(self.file_type),
262271
)
263272
order = 0
264273
for author in draft.get_authors():
@@ -297,3 +306,163 @@ def get_author_buttons(self):
297306
'email': i.email()[1],
298307
'full_name': full_name})
299308
return ''.join(buttons)
309+
310+
def save(self, request):
311+
self.save_submitter_info()
312+
self.save_new_draft_info()
313+
self.send_confirmation_mail(request)
314+
315+
def send_confirmation_mail(self, request):
316+
subject = 'Confirmation for Auto-Post of I-D %s' % self.draft.filename
317+
from_email = settings.IDST_FROM_EMAIL
318+
to_email = self.cleaned_data['email']
319+
send_mail(request, from_email, to_email, subject, 'submit/confirm_autopost.txt',
320+
{'draft': self.draft })
321+
322+
def save_submitter_info(self):
323+
TempIdAuthors.objects.create(
324+
id_document_tag=self.draft.temp_id_document_tag,
325+
first_name=self.cleaned_data['first_name'],
326+
last_name=self.cleaned_data['last_name'],
327+
email_address=self.cleaned_data['email'],
328+
author_order=0,
329+
submission=self.draft)
330+
331+
def save_new_draft_info(self):
332+
salt = sha.new(str(random.random())).hexdigest()[:5]
333+
self.draft.auth_key = sha.new(salt+self.cleaned_data['email']).hexdigest()
334+
self.draft.status_id = WAITING_AUTHENTICATION
335+
self.draft.save()
336+
337+
338+
class MetaDataForm(AutoPostForm):
339+
340+
title = forms.CharField(label=u'Title', required=True)
341+
version = forms.CharField(label=u'Version', required=True)
342+
creation_date = forms.DateField(label=u'Creation date', required=True)
343+
pages = forms.IntegerField(label=u'Pages', required=True)
344+
abstract = forms.CharField(label=u'Abstract', widget=forms.Textarea, required=True)
345+
first_name = forms.CharField(label=u'Given name', required=True)
346+
last_name = forms.CharField(label=u'Last name', required=True)
347+
email = forms.EmailField(label=u'Email address', required=True)
348+
comments = forms.CharField(label=u'Comments to the secretariat', widget=forms.Textarea, required=False)
349+
fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'first_name', 'last_name', 'email', 'comments']
350+
351+
def __init__(self, *args, **kwargs):
352+
super(MetaDataForm, self).__init__(*args, **kwargs)
353+
self.set_initials()
354+
self.authors = self.get_initial_authors()
355+
356+
def get_initial_authors(self):
357+
authors=[]
358+
if self.is_bound:
359+
for key, value in self.data.items():
360+
if key.startswith('first_name_'):
361+
author = {'errors': {}}
362+
index = key.replace('first_name_', '')
363+
first_name = value.strip()
364+
if not first_name:
365+
author['errors']['first_name'] = 'This field is required'
366+
last_name = self.data.get('last_name_%s' % index, '').strip()
367+
if not last_name:
368+
author['errors']['last_name'] = 'This field is required'
369+
email = self.data.get('email_%s' % index, '').strip()
370+
if not email:
371+
author['errors']['email'] = 'This field is required'
372+
elif not email_re.search(email):
373+
author['errors']['email'] = 'Enter a valid e-mail address'
374+
if first_name or last_name or email:
375+
author.update({'first_name': first_name,
376+
'last_name': last_name,
377+
'email': ('%s %s' % (first_name, last_name), email),
378+
'index': index,
379+
})
380+
authors.append(author)
381+
authors.sort(lambda x,y: cmp(int(x['index']), int(y['index'])))
382+
return authors
383+
384+
def set_initials(self):
385+
self.fields['pages'].initial=self.draft.txt_page_count
386+
self.fields['creation_date'].initial=self.draft.creation_date
387+
self.fields['version'].initial=self.draft.revision
388+
self.fields['abstract'].initial=self.draft.abstract
389+
self.fields['title'].initial=self.draft.id_document_name
390+
391+
def clean_creation_date(self):
392+
creation_date = self.cleaned_data.get('creation_date', None)
393+
if not creation_date:
394+
return None
395+
submit_date = self.draft.submission_date
396+
if creation_date > submit_date:
397+
raise forms.ValidationError('Creation Date must not be set after submission date')
398+
if creation_date + datetime.timedelta(days=3) < submit_date:
399+
raise forms.ValidationError('Creation Date must be within 3 days of submission date')
400+
return creation_date
401+
402+
def clean_version(self):
403+
version = self.cleaned_data.get('version', None)
404+
if not version:
405+
return None
406+
if len(version) > 2:
407+
raise forms.ValidationError('Version field is not in NN format')
408+
try:
409+
version_int = int(version)
410+
except ValueError:
411+
raise forms.ValidationError('Version field is not in NN format')
412+
if version_int > 99 or version_int < 0:
413+
raise forms.ValidationError('Version must be set between 00 and 99')
414+
existing_revisions = [int(i.revision) for i in InternetDraft.objects.filter(filename=self.draft.filename)]
415+
expected = 0
416+
if existing_revisions:
417+
expected = max(existing_revisions) + 1
418+
if version_int != expected:
419+
raise forms.ValidationError('Invalid Version Number (Version %00d is expected)' % expected)
420+
return version
421+
422+
def clean(self):
423+
if bool([i for i in self.authors if i['errors']]):
424+
raise forms.ValidationError('Please fix errors in author list')
425+
return super(MetaDataForm, self).clean()
426+
427+
def get_authors(self):
428+
if not self.is_bound:
429+
return self.validation.get_authors()
430+
else:
431+
return self.authors
432+
433+
def move_docs(self, draft, revision):
434+
old_revision = draft.revision
435+
for ext in draft.file_type.split(','):
436+
source = os.path.join(settings.STAGING_PATH, '%s-%s%s' % (draft.filename, old_revision, ext))
437+
dest = os.path.join(settings.STAGING_PATH, '%s-%s%s' % (draft.filename, revision, ext))
438+
os.rename(source, dest)
439+
440+
def save_new_draft_info(self):
441+
draft = self.draft
442+
draft.id_documen_name = self.cleaned_data['title']
443+
if draft.revision != self.cleaned_data['version']:
444+
self.move_docs(draft, self.cleaned_data['version'])
445+
draft.revision = self.cleaned_data['version']
446+
draft.creation_date = self.cleaned_data['creation_date']
447+
draft.txt_page_count = self.cleaned_data['pages']
448+
draft.abstract = self.cleaned_data['abstract']
449+
draft.comment_to_sec = self.cleaned_data['comments']
450+
draft.status_id = MANUAL_POST_REQUESTED
451+
draft.save()
452+
self.save_submitter_info()
453+
454+
def save(self, request):
455+
self.save_new_draft_info()
456+
self.send_mail_to_secretariat(request)
457+
458+
def send_mail_to_secretariat(self, request):
459+
subject = 'Manual Post Requested for %s' % self.draft.filename
460+
from_email = settings.IDST_FROM_EMAIL
461+
to_email = settings.IDST_TO_EMAIL
462+
cc = [self.cleaned_data['email']]
463+
cc += [i['email'][1] for i in self.authors]
464+
if self.draft.group_acronym:
465+
cc += [i.person.email()[1] for i in self.draft.group_acronym.wgchair_set.all()]
466+
cc = list(set(cc))
467+
send_mail(request, from_email, to_email, subject, 'submit/manual_post_mail.txt',
468+
{'form': self, 'draft': self.draft }, cc=cc)

ietf/submit/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
url(r'^status/$', 'submit_status', name='submit_status'),
77
url(r'^status/(?P<submission_id>\d+)/$', 'draft_status', name='draft_status'),
88
url(r'^status/(?P<submission_id>\d+)/edit/$', 'draft_edit', name='draft_edit'),
9+
url(r'^status/(?P<submission_id>\d+)/confirm/(?P<auth_key>[a-f\d]+)/$', 'draft_confirm', name='draft_confirm'),
910
)
1011

1112
urlpatterns += patterns('django.views.generic.simple',

ietf/submit/utils.py

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,62 @@
1+
import os
12
import re
23
import datetime
34

4-
from ietf.idtracker.models import InternetDraft, EmailAddress
5+
from ietf.idtracker.models import InternetDraft, EmailAddress, PersonOrOrgInfo
6+
7+
8+
# Some usefull states
9+
UPLOADED = 1
10+
WAITING_AUTHENTICATION = 4
11+
MANUAL_POST_REQUESTED = 5
12+
POSTED = -1
13+
POSTED_BY_SECRETARIAT = -2
14+
15+
16+
# Not a real WG
17+
NONE_WG = 1027
18+
19+
20+
def perform_post(submission):
21+
group_id = submission.group_acronym and submission.group_acronym.pk or NONE_WG
22+
updated = False
23+
try:
24+
draft = InternetDraft.objects.get(filename=submission.filename)
25+
draft.title = submission.id_document_name
26+
draft.group_id = group_id
27+
draft.filename = submission.filename
28+
draft.revision = submission.revision
29+
draft.revision_date = submission.creation_date
30+
draft.file_type = submission.file_type
31+
draft.txt_page_count = submission.txt_page_count
32+
draft.last_modified_date = datetime.date.today()
33+
draft.abstract = submission.abstract
34+
draft.save()
35+
updated = True
36+
except InternetDraft.DoesNotExist:
37+
draft = InternetDraft.objects.create(
38+
title = submission.id_document_name,
39+
group_id = group_id,
40+
filename = submission.filename,
41+
revision = submission.revision,
42+
revision_date = submission.creation_date,
43+
file_type = submission.file_type,
44+
txt_page_count = submission.txt_page_count,
45+
start_date = datetime.date.today(),
46+
last_modified_date = datetime.date.today(),
47+
abstract = submission.abstract,
48+
status_id = 1, # Active
49+
intended_status_id = 8, # None
50+
)
51+
move_docs(submission)
52+
submission.status_id = POSTED
53+
submission.save()
54+
55+
def move_docs(submission):
56+
for ext in submission.file_type.split(','):
57+
source = os.path.join(settings.STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
58+
dest = os.path.join(settings.INTERNET_DRAFT_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
59+
os.rename(source, dest)
560

661

762
class DraftValidation(object):
@@ -12,13 +67,14 @@ def __init__(self, draft):
1267
self.passes_idnits = self.passes_idnits()
1368
self.wg = self.get_working_group()
1469
self.authors = self.get_authors()
70+
self.submitter = self.get_submitter()
1571

1672
def passes_idnits(self):
1773
passes_idnits = self.check_idnits_success(self.draft.idnits_message)
1874
return passes_idnits
1975

2076
def get_working_group(self):
21-
if self.draft.group_acronym and self.draft.group_acronym.pk == 1027:
77+
if self.draft.group_acronym and self.draft.group_acronym.pk == NONE_WG:
2278
return None
2379
return self.draft.group_acronym
2480

@@ -40,12 +96,19 @@ def is_valid(self):
4096
def validate_metadata(self):
4197
self.validate_revision()
4298
self.validate_authors()
99+
self.validate_abstract()
43100
self.validate_creation_date()
44101

102+
def validate_abstract(self):
103+
if not self.draft.abstract:
104+
self.add_warning('abstract', 'Abstract is empty or was not found')
105+
45106
def add_warning(self, key, value):
46107
self.warnings.update({key: value})
47108

48109
def validate_revision(self):
110+
if self.draft.status_id in [POSTED, POSTED_BY_SECRETARIAT]:
111+
return
49112
revision = self.draft.revision
50113
existing_revisions = [int(i.revision) for i in InternetDraft.objects.filter(filename=self.draft.filename)]
51114
expected = 0
@@ -61,24 +124,25 @@ def validate_authors(self):
61124
def validate_creation_date(self):
62125
date = self.draft.creation_date
63126
if not date:
64-
self.add_warning('creation_date', 'Creation Date field is empty or the creation date is not in a proper format.')
127+
self.add_warning('creation_date', 'Creation Date field is empty or the creation date is not in a proper format')
65128
return
66129
submit_date = self.draft.submission_date
67-
if date + datetime.timedelta(days=3) > submit_date:
68-
self.add_warning('creation_date', 'Creation Date must be within 3 days of submission date.')
130+
if date > submit_date:
131+
self.add_warning('creation_date', 'Creation Date must not be set after submission date')
132+
if date + datetime.timedelta(days=3) < submit_date:
133+
self.add_warning('creation_date', 'Creation Date must be within 3 days of submission date')
69134

70135
def get_authors(self):
71-
tmpauthors = self.draft.tempidauthors_set.all().order_by('author_order')
72-
authors = []
73-
for i in tmpauthors:
74-
person = None
75-
for existing in EmailAddress.objects.filter(address=i.email_address):
76-
try:
77-
person = existing.person_or_org
78-
except PersonOrOrgInfo.DoesNotExist:
79-
pass
80-
if not person:
81-
authors.append(i)
82-
else:
83-
authors.append(person)
84-
return authors
136+
tmpauthors = self.draft.tempidauthors_set.exclude(author_order=0).order_by('author_order')
137+
return tmpauthors
138+
139+
def get_submitter(self):
140+
submitter = self.draft.tempidauthors_set.filter(author_order=0)
141+
if submitter:
142+
return submitter[0]
143+
elif self.draft.submitter_tag:
144+
try:
145+
return PersonOrOrgInfo.objects.get(pk=self.draft.submitter_tag)
146+
except PersonOrOrgInfo.DoesNotExist:
147+
return False
148+
return None

0 commit comments

Comments
 (0)