Skip to content

Commit d56e446

Browse files
committed
Port edit info view with tests to new schema
- Legacy-Id: 2823
1 parent 4b15644 commit d56e446

10 files changed

Lines changed: 451 additions & 104 deletions

File tree

ietf/idrfc/fixtures/names.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,11 @@
371371
<field type="TextField" name="desc"></field>
372372
<field type="BooleanField" name="used">1</field>
373373
</object>
374+
<object pk="ex-ad" model="name.rolename">
375+
<field type="CharField" name="name">Ex-Area Director</field>
376+
<field type="TextField" name="desc">In-active Area Director</field>
377+
<field type="BooleanField" name="used">1</field>
378+
</object>
374379
<object pk="std" model="name.stdlevelname">
375380
<field type="CharField" name="name">Standard</field>
376381
<field type="TextField" name="desc"></field>

ietf/idrfc/mails.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def email_ownerREDESIGN(request, doc, owner, changed_by, text, subject=None):
5454
to = owner.formatted_email()
5555
send_mail(request, to,
5656
"DraftTracker Mail System <iesg-secretary@ietf.org>",
57-
"%s updated by %s" % (doc.file_tag(), changed_by),
57+
"%s updated by %s" % (doc.file_tag(), changed_by.get_name()),
5858
"idrfc/change_notice.txt",
5959
dict(text=html_to_text(text),
6060
doc=doc,

ietf/idrfc/testsREDESIGN.py

Lines changed: 106 additions & 96 deletions
Large diffs are not rendered by default.

ietf/idrfc/utils.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from django.conf import settings
2+
13
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
24
from ietf.idrfc.mails import *
35

@@ -92,3 +94,57 @@ def update_telechat(request, idinternal, new_telechat_date, new_returning_item=N
9294
(new_telechat_date,
9395
idinternal.telechat_date))
9496
idinternal.telechat_date = new_telechat_date
97+
98+
def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_item=None):
99+
on_agenda = bool(new_telechat_date)
100+
101+
from doc.models import Telechat
102+
prev = doc.latest_event(Telechat, type="scheduled_for_telechat")
103+
prev_returning = bool(prev and prev.returning_item)
104+
prev_telechat = prev.telechat_date if prev else None
105+
prev_agenda = bool(prev_telechat)
106+
107+
returning_item_changed = bool(new_returning_item != None and new_returning_item != prev_returning)
108+
109+
if new_returning_item == None:
110+
returning = prev_returning
111+
else:
112+
returning = new_returning_item
113+
114+
if returning == prev_returning and new_telechat_date == prev_telechat:
115+
# fully updated, nothing to do
116+
return
117+
118+
# auto-update returning item
119+
if (not returning_item_changed and on_agenda and prev_agenda
120+
and new_telechat_date != prev_telechat):
121+
returning = True
122+
123+
e = Telechat()
124+
e.type = "scheduled_for_telechat"
125+
e.by = by
126+
e.doc = doc
127+
e.returning_item = returning
128+
e.telechat_date = new_telechat_date
129+
130+
if on_agenda != prev_agenda:
131+
if on_agenda:
132+
e.desc = "Placed on agenda for telechat - %s by %s" % (
133+
new_telechat_date, by.get_name())
134+
else:
135+
e.desc = "Removed from agenda for telechat by %s" % by.get_name()
136+
elif on_agenda and new_telechat_date != prev_telechat:
137+
e.desc = "Telechat date has been changed to <b>%s</b> from <b>%s</b> by %s" % (
138+
new_telechat_date, prev_telechat, by.get_name())
139+
else:
140+
# we didn't reschedule but flipped returning item bit - let's
141+
# just explain that
142+
if returning:
143+
e.desc = "Added as returning item on telechat by %s" % by.get_name()
144+
else:
145+
e.desc = "Removed as returning item on telechat by %s" % by.get_name()
146+
147+
e.save()
148+
149+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
150+
update_telechat = update_telechatREDESIGN

ietf/idrfc/views_doc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def _get_history(doc, versions):
152152
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
153153
versions = [] # clear versions
154154
event_holder = doc._draft if hasattr(doc, "_draft") else doc._rfcindex
155-
for e in event_holder.event_set.all().select_related('by').order_by('-time', '-id'):
155+
for e in event_holder.event_set.all().select_related('by').order_by('-time', 'id'):
156156
info = {}
157157
if e.type == "new_revision":
158158
filename = u"%s-%s" % (e.doc.name, e.newrevision.rev)

ietf/idrfc/views_edit.py

Lines changed: 231 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
from ietf.idrfc.utils import *
2323
from ietf.idrfc.lastcall import request_last_call
2424

25-
from doc.models import Document, Event, save_document_in_history, DocHistory
26-
from name.models import IesgDocStateName, get_next_iesg_states
25+
from doc.models import Document, Event, Status, Telechat, save_document_in_history, DocHistory
26+
from name.models import IesgDocStateName, IntendedStdLevelName, DocInfoTagName, get_next_iesg_states
2727

2828
class ChangeStateForm(forms.Form):
2929
state = forms.ModelChoiceField(IDState.objects.all(), empty_label=None, required=True)
@@ -90,7 +90,7 @@ def change_stateREDESIGN(request, name):
9090
"""Change state of Internet Draft, notifying parties as necessary
9191
and logging the change as a comment."""
9292
doc = get_object_or_404(Document, docalias__name=name)
93-
if not doc.latest_event(type="started_iesg_process") or doc.state_id == "expired":
93+
if (not doc.latest_event(type="started_iesg_process")) or doc.state_id == "expired":
9494
raise Http404()
9595

9696
login = request.user.get_profile().email()
@@ -379,6 +379,234 @@ def diff(obj, attr, name):
379379
login=login),
380380
context_instance=RequestContext(request))
381381

382+
class NameFromEmailModelChoiceField(forms.ModelChoiceField):
383+
def label_from_instance(self, obj):
384+
return obj.get_name()
385+
386+
class EditInfoFormREDESIGN(forms.Form):
387+
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.all(), empty_label=None, required=True)
388+
status_date = forms.DateField(required=False, help_text="Format is YYYY-MM-DD")
389+
via_rfc_editor = forms.BooleanField(required=False, label="Via IRTF or RFC Editor")
390+
ad = NameFromEmailModelChoiceField(Email.objects.filter(role__name__in=("ad", "ex-ad")).order_by('role__name', 'person__name'), label="Responsible AD", empty_label=None, required=True)
391+
notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False)
392+
note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False)
393+
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False)
394+
returning_item = forms.BooleanField(required=False)
395+
396+
def __init__(self, *args, **kwargs):
397+
old_ads = kwargs.pop('old_ads')
398+
399+
super(self.__class__, self).__init__(*args, **kwargs)
400+
401+
# fix up ad field
402+
choices = self.fields['ad'].choices
403+
ex_ads = dict((e.pk, e) for e in Email.objects.filter(role__name="ex-ad"))
404+
if old_ads:
405+
# separate active ADs from inactive
406+
for i, t in enumerate(choices):
407+
if t[0] in ex_ads:
408+
choices.insert(i, ("", "----------------"))
409+
break
410+
else:
411+
# remove old ones
412+
self.fields['ad'].choices = [t for t in choices if t[0] not in ex_ads]
413+
414+
# telechat choices
415+
dates = TelechatDates.objects.all()[0].dates()
416+
init = kwargs['initial']['telechat_date']
417+
if init and init not in dates:
418+
dates.insert(0, init)
419+
420+
choices = [("", "(not on agenda)")]
421+
for d in dates:
422+
choices.append((d, d.strftime("%Y-%m-%d")))
423+
424+
self.fields['telechat_date'].choices = choices
425+
426+
# returning item is rendered non-standard
427+
self.standard_fields = [x for x in self.visible_fields() if x.name not in ('returning_item',)]
428+
429+
def clean_status_date(self):
430+
d = self.cleaned_data['status_date']
431+
if d:
432+
if d < date.today():
433+
raise forms.ValidationError("Date must not be in the past.")
434+
if d >= date.today() + timedelta(days=365 * 2):
435+
raise forms.ValidationError("Date must be within two years.")
436+
437+
return d
438+
439+
def clean_note(self):
440+
# note is stored munged in the database
441+
return self.cleaned_data['note'].replace('\n', '<br>').replace('\r', '').replace(' ', '&nbsp; ')
442+
443+
444+
def get_initial_notify(doc):
445+
# set change state notice to something sensible
446+
receivers = []
447+
if doc.group.type_id == "individ":
448+
for a in doc.authors.all():
449+
receivers.append(e.address)
450+
else:
451+
receivers.append("%s-chairs@%s" % (doc.group.acronym, settings.TOOLS_SERVER))
452+
for editor in Email.objects.filter(role__name="wgeditor", role__group=doc.group):
453+
receivers.append(e.address)
454+
455+
receivers.append("%s@%s" % (doc.name, settings.TOOLS_SERVER))
456+
return ", ".join(receivers)
457+
458+
@group_required('Area_Director','Secretariat')
459+
def edit_infoREDESIGN(request, name):
460+
"""Edit various Internet Draft attributes, notifying parties as
461+
necessary and logging changes as document events."""
462+
doc = get_object_or_404(Document, name=name)
463+
if doc.state_id == "expired":
464+
raise Http404()
465+
466+
login = request.user.get_profile().email()
467+
468+
new_document = False
469+
if not doc.iesg_state: # FIXME: should probably get this as argument to view
470+
new_document = True
471+
doc.iesg_state = IesgDocStateName.objects.get(slug="pub-req")
472+
doc.notify = get_initial_notify(doc)
473+
474+
e = doc.latest_event(Telechat, type="scheduled_for_telechat")
475+
initial_telechat_date = e.telechat_date if e else None
476+
initial_returning_item = bool(e and e.returning_item)
477+
478+
if request.method == 'POST':
479+
form = EditInfoForm(request.POST,
480+
old_ads=False,
481+
initial=dict(telechat_date=initial_telechat_date))
482+
if form.is_valid():
483+
save_document_in_history(doc)
484+
485+
r = form.cleaned_data
486+
if new_document:
487+
# fix so Django doesn't barf in the diff below because these
488+
# fields can't be NULL
489+
doc.ad = r['ad']
490+
491+
replaces = Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces")
492+
if replaces:
493+
# this should perhaps be somewhere else, e.g. the
494+
# place where the replace relationship is established
495+
e = Event()
496+
e.type = "added_comment"
497+
e.by = Email.objects.get(address="(System)")
498+
e.doc = doc
499+
e.desc = "Earlier history may be found in the Comment Log for <a href=\"%s\">%s</a>" % (replaces[0], replaces[0].get_absolute_url())
500+
e.save()
501+
502+
e = Event()
503+
e.type = "started_iesg_process"
504+
e.by = login
505+
e.doc = doc
506+
e.desc = "IESG process started in state <b>%s</b>" % doc.iesg_state.name
507+
e.save()
508+
509+
orig_ad = doc.ad
510+
511+
changes = []
512+
513+
def desc(attr, new, old):
514+
entry = "%(attr)s has been changed to <b>%(new)s</b> from <b>%(old)s</b>"
515+
if new_document:
516+
entry = "%(attr)s has been changed to <b>%(new)s</b>"
517+
518+
return entry % dict(attr=attr, new=new, old=old)
519+
520+
def diff(attr, name):
521+
v = getattr(doc, attr)
522+
if r[attr] != v:
523+
changes.append(desc(name, r[attr], v))
524+
setattr(doc, attr, r[attr])
525+
526+
# update the attributes, keeping track of what we're doing
527+
diff('intended_std_level', "Intended Status")
528+
diff('ad', "Responsible AD")
529+
diff('notify', "State Change Notice email list")
530+
531+
if r['note'] != doc.note:
532+
if not r['note']:
533+
if doc.note:
534+
changes.append("Note field has been cleared")
535+
else:
536+
if doc.note:
537+
changes.append("Note changed to '%s'" % r['note'])
538+
else:
539+
changes.append("Note added '%s'" % r['note'])
540+
541+
doc.note = r['note']
542+
543+
for c in changes:
544+
e = Event()
545+
e.type = "changed_document"
546+
e.by = login
547+
e.doc = doc
548+
e.desc = c + " by %s" % login.get_name()
549+
e.save()
550+
551+
update_telechat(request, doc, login,
552+
r['telechat_date'], r['returning_item'])
553+
554+
e = doc.latest_event(Status, type="changed_status_date")
555+
status_date = e.date if e else None
556+
if r["status_date"] != status_date:
557+
e = Status()
558+
e.type ="changed_status_date"
559+
e.by = login
560+
e.doc = doc
561+
d = desc("Status date", r["status_date"], status_date)
562+
changes.append(d)
563+
e.desc = d + " by %s" % login.get_name()
564+
e.date = r["status_date"]
565+
e.save()
566+
567+
if in_group(request.user, 'Secretariat'):
568+
via_rfc = DocInfoTagName.objects.get(slug="via-rfc")
569+
if r['via_rfc_editor']:
570+
doc.tags.add(via_rfc)
571+
else:
572+
doc.tags.remove(via_rfc)
573+
574+
doc.time = datetime.datetime.now()
575+
576+
if changes and not new_document:
577+
email_owner(request, doc, orig_ad, login, "\n".join(changes))
578+
579+
doc.save()
580+
return HttpResponseRedirect(doc.get_absolute_url())
581+
else:
582+
e = doc.latest_event(Status)
583+
status = e.date if e else None
584+
init = dict(intended_std_level=doc.intended_std_level,
585+
status_date=status,
586+
ad=doc.ad,
587+
notify=doc.notify,
588+
note=dehtmlify_textarea_text(doc.note),
589+
telechat_date=initial_telechat_date,
590+
returning_item=initial_returning_item,
591+
)
592+
593+
form = EditInfoForm(old_ads=False, initial=init)
594+
595+
if not in_group(request.user, 'Secretariat'):
596+
# filter out Via RFC Editor
597+
form.standard_fields = [x for x in form.standard_fields if x.name != "via_rfc_editor"]
598+
599+
return render_to_response('idrfc/edit_infoREDESIGN.html',
600+
dict(doc=doc,
601+
form=form,
602+
user=request.user,
603+
login=login),
604+
context_instance=RequestContext(request))
605+
606+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
607+
EditInfoForm = EditInfoFormREDESIGN
608+
edit_info = edit_infoREDESIGN
609+
382610

383611
@group_required('Area_Director','Secretariat')
384612
def request_resurrect(request, name):
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}Edit info on {{ doc }}{% endblock %}
4+
5+
{% block morecss %}
6+
form.edit-info #id_state_change_notice_to {
7+
width: 600px;
8+
}
9+
form.edit-info #id_note {
10+
width: 600px;
11+
height: 150px;
12+
}
13+
form.edit-info .actions {
14+
padding-top: 20px;
15+
}
16+
{% endblock %}
17+
18+
{% block content %}
19+
{% load ietf_filters %}
20+
<h1>Edit info on {{ doc }}</h1>
21+
22+
<form class="edit-info" action="" method="POST">
23+
<table>
24+
{% for field in form.standard_fields %}
25+
<tr>
26+
<th>{{ field.label_tag }}:</th>
27+
<td>{{ field }}
28+
{% ifequal field.name "telechat_date" %}{{ form.returning_item }} {{ form.returning_item.label_tag }} {{ form.returning_item.errors }}{% endifequal %}
29+
{% ifequal field.name "ad" %}
30+
{% if user|in_group:"Area_Director" %}
31+
<label><input type="checkbox" name="ad" value="{{ login.pk }}" /> Assign to me</label>
32+
{% endif %}
33+
{% endifequal %}
34+
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
35+
{{ field.errors }}</td>
36+
</tr>
37+
{% endfor %}
38+
<tr>
39+
<td></td>
40+
<td class="actions">
41+
<a href="{{ doc.get_absolute_url }}">Back</a>
42+
<input type="submit" value="Save"/>
43+
</td>
44+
</tr>
45+
</table>
46+
</form>
47+
{% endblock %}

0 commit comments

Comments
 (0)