Skip to content

Commit 63d96e0

Browse files
committed
Summary: Get rid of submit_initial_charter hack, only create a charter
when a first revision has actually been submitted. - Legacy-Id: 10136
1 parent f496f4c commit 63d96e0

9 files changed

Lines changed: 108 additions & 104 deletions

File tree

ietf/doc/tests_charter.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
from ietf.doc.models import ( Document, State, BallotDocEvent, BallotType, NewRevisionDocEvent,
1111
TelechatDocEvent, WriteupDocEvent )
12-
from ietf.doc.utils_charter import next_revision, default_review_text, default_action_text
12+
from ietf.doc.utils_charter import ( next_revision, default_review_text, default_action_text,
13+
charter_name_for_group )
1314
from ietf.group.models import Group, GroupMilestone
1415
from ietf.iesg.models import TelechatDate
1516
from ietf.person.models import Person
@@ -270,6 +271,40 @@ def test_submit_charter(self):
270271
self.assertEqual(f.read(),
271272
"Windows line\nMac line\nUnix line\n" + utf_8_snippet)
272273

274+
def test_submit_initial_charter(self):
275+
make_test_data()
276+
277+
group = Group.objects.get(acronym="mars")
278+
# get rid of existing charter
279+
charter = group.charter
280+
group.charter = None
281+
group.save()
282+
charter.delete()
283+
charter = None
284+
285+
url = urlreverse('charter_submit', kwargs=dict(name=charter_name_for_group(group)))
286+
login_testing_unauthorized(self, "secretary", url)
287+
288+
# normal get
289+
r = self.client.get(url)
290+
self.assertEqual(r.status_code, 200)
291+
q = PyQuery(r.content)
292+
self.assertEqual(len(q('form input[name=txt]')), 1)
293+
294+
# create charter
295+
test_file = StringIO("Simple test")
296+
test_file.name = "unnamed"
297+
298+
r = self.client.post(url, dict(txt=test_file))
299+
self.assertEqual(r.status_code, 302)
300+
301+
charter = Document.objects.get(name="charter-ietf-%s" % group.acronym)
302+
self.assertEqual(charter.rev, "00-00")
303+
self.assertTrue("new_revision" in charter.latest_event().type)
304+
305+
group = Group.objects.get(pk=group.pk)
306+
self.assertEqual(group.charter, charter)
307+
273308
def test_edit_announcement_text(self):
274309
draft = make_test_data()
275310
charter = draft.group.charter

ietf/doc/utils_charter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def charter_name_for_group(group):
2121

2222
return "charter-%s-%s" % (top_org, group.acronym)
2323

24+
def split_charter_name(charter_name):
25+
top_org, group_acronym = charter_name.split("-")[1:]
26+
return top_org, group_acronym
27+
2428
def next_revision(rev):
2529
if rev == "":
2630
return "00-00"

ietf/doc/views_charter.py

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os, datetime, textwrap, json
22

33
from django.http import HttpResponseRedirect, HttpResponseNotFound, HttpResponseForbidden, Http404
4-
from django.shortcuts import render_to_response, get_object_or_404, redirect
4+
from django.shortcuts import render_to_response, get_object_or_404, redirect, render
55
from django.core.urlresolvers import reverse as urlreverse
66
from django.template import RequestContext
77
from django import forms
@@ -12,16 +12,17 @@
1212

1313
import debug # pyflakes:ignore
1414

15-
from ietf.doc.models import ( Document, DocHistory, State, DocEvent, BallotDocEvent,
16-
BallotPositionDocEvent, InitialReviewDocEvent, NewRevisionDocEvent,
15+
from ietf.doc.models import ( Document, DocAlias, DocHistory, State, DocEvent,
16+
BallotDocEvent, BallotPositionDocEvent, InitialReviewDocEvent, NewRevisionDocEvent,
1717
WriteupDocEvent )
1818
from ietf.doc.utils import ( add_state_change_event, close_open_ballots,
1919
create_ballot_if_not_open, get_chartering_type )
2020
from ietf.doc.utils_charter import ( historic_milestones_for_charter,
2121
approved_revision, default_review_text, default_action_text, email_state_changed,
2222
generate_ballot_writeup, generate_issue_ballot_mail, next_revision,
23-
change_group_state_after_charter_approval, fix_charter_revision_after_approval)
24-
from ietf.group.models import ChangeStateGroupEvent, MilestoneGroupEvent
23+
change_group_state_after_charter_approval, fix_charter_revision_after_approval,
24+
split_charter_name)
25+
from ietf.group.models import Group, ChangeStateGroupEvent, MilestoneGroupEvent
2526
from ietf.group.utils import save_group_in_history, save_milestone_in_history, can_manage_group_type
2627
from ietf.ietfauth.utils import has_role, role_required
2728
from ietf.name.models import GroupStateName
@@ -346,22 +347,31 @@ def save(self, group, rev):
346347
destination.write(self.cleaned_data['content'].encode("utf-8"))
347348

348349
@login_required
349-
def submit(request, name=None, option=None):
350+
def submit(request, name, option=None):
350351
if not name.startswith('charter-'):
351352
raise Http404
352353

353-
charter = get_object_or_404(Document, type="charter", name=name)
354-
group = charter.group
354+
charter = Document.objects.filter(type="charter", name=name).first()
355+
if charter:
356+
group = charter.group
357+
charter_canonical_name = charter.canonical_name()
358+
charter_rev = charter.rev
359+
else:
360+
top_org, group_acronym = split_charter_name(name)
361+
group = get_object_or_404(Group, acronym=group_acronym)
362+
charter_canonical_name = name
363+
charter_rev = "00-00"
355364

356-
if not can_manage_group_type(request.user, group.type_id):
365+
if not can_manage_group_type(request.user, group.type_id) or not group.features.has_chartering_process:
357366
return HttpResponseForbidden("You don't have permission to access this view")
358367

359-
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev))
360-
not_uploaded_yet = charter.rev.endswith("-00") and not os.path.exists(path)
361368

362-
if not_uploaded_yet:
369+
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter_canonical_name, charter_rev))
370+
not_uploaded_yet = charter_rev.endswith("-00") and not os.path.exists(path)
371+
372+
if not_uploaded_yet or not charter:
363373
# this case is special - we recently chartered or rechartered and have no file yet
364-
next_rev = charter.rev
374+
next_rev = charter_rev
365375
else:
366376
# search history for possible collisions with abandoned efforts
367377
prev_revs = list(charter.history_set.order_by('-time').values_list('rev', flat=True))
@@ -375,7 +385,23 @@ def submit(request, name=None, option=None):
375385
# Also save group history so we can search for it
376386
save_group_in_history(group)
377387

378-
charter.rev = next_rev
388+
if not charter:
389+
charter = Document.objects.create(
390+
name=name,
391+
type_id="charter",
392+
title=group.name,
393+
group=group,
394+
abstract=group.name,
395+
rev=next_rev,
396+
)
397+
DocAlias.objects.create(name=charter.name, document=charter)
398+
399+
charter.set_state(State.objects.get(used=True, type="charter", slug="notrev"))
400+
401+
group.charter = charter
402+
group.save()
403+
else:
404+
charter.rev = next_rev
379405

380406
events = []
381407
e = NewRevisionDocEvent(doc=charter, by=request.user.person, type="new_revision")
@@ -397,30 +423,30 @@ def submit(request, name=None, option=None):
397423
else:
398424
return redirect("doc_view", name=charter.name)
399425
else:
400-
init = { "content": ""}
401-
c = charter
426+
init = { "content": "" }
402427

403-
if not_uploaded_yet:
428+
if not_uploaded_yet and charter:
404429
# use text from last approved revision
405430
last_approved = charter.rev.split("-")[0]
406-
h = charter.history_set.filter(rev=last_approved).order_by("-time", "-id")
431+
h = charter.history_set.filter(rev=last_approved).order_by("-time", "-id").first()
407432
if h:
408-
c = h[0]
433+
charter_canonical_name = h.canonical_name()
434+
charter_rev = h.rev
409435

410-
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (c.canonical_name(), c.rev))
436+
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter_canonical_name, charter_rev))
411437

412438
try:
413439
with open(filename, 'r') as f:
414440
init["content"] = f.read()
415441
except IOError:
416442
pass
417443
form = UploadForm(initial=init)
418-
return render_to_response('doc/charter/submit.html',
419-
{'form': form,
420-
'next_rev': next_rev,
421-
'group': group,
422-
'name': name },
423-
context_instance=RequestContext(request))
444+
return render(request, 'doc/charter/submit.html', {
445+
'form': form,
446+
'next_rev': next_rev,
447+
'group': group,
448+
'name': name,
449+
})
424450

425451
class AnnouncementTextForm(forms.Form):
426452
announcement_text = forms.CharField(widget=forms.Textarea, required=True)

ietf/group/edit.py

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import debug # pyflakes:ignore
1313

14-
from ietf.doc.models import Document, DocAlias, DocTagName, State
14+
from ietf.doc.models import DocTagName, State
1515
from ietf.doc.utils import get_tags_for_stream_id
1616
from ietf.doc.utils_charter import charter_name_for_group
1717
from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStateName,
@@ -144,57 +144,6 @@ def format_urls(urls, fs="\n"):
144144
res.append(u.url)
145145
return fs.join(res)
146146

147-
def get_or_create_initial_charter(group, group_type):
148-
charter_name = charter_name_for_group(group)
149-
150-
try:
151-
charter = Document.objects.get(docalias__name=charter_name)
152-
except Document.DoesNotExist:
153-
charter = Document.objects.create(
154-
name=charter_name,
155-
type_id="charter",
156-
title=group.name,
157-
group=group,
158-
abstract=group.name,
159-
rev="00-00",
160-
)
161-
charter.set_state(State.objects.get(used=True, type="charter", slug="notrev"))
162-
163-
# Create an alias as well
164-
DocAlias.objects.create(name=charter.name, document=charter)
165-
166-
return charter
167-
168-
@login_required
169-
def submit_initial_charter(request, group_type=None, acronym=None):
170-
171-
# This needs refactoring.
172-
# The signature assumed you could have groups with the same name, but with different types, which we do not allow.
173-
# Consequently, this can be called with an existing group acronym and a type
174-
# that doesn't match the existing group type. The code below essentially ignores the group_type argument.
175-
#
176-
# If possible, the use of get_or_create_initial_charter should be moved
177-
# directly into charter_submit, and this function should go away.
178-
179-
if acronym==None:
180-
raise Http404
181-
182-
group = get_object_or_404(Group, acronym=acronym)
183-
if not group.features.has_chartering_process:
184-
raise Http404
185-
186-
# This is where we start ignoring the passed in group_type
187-
group_type = group.type_id
188-
189-
if not can_manage_group_type(request.user, group_type):
190-
return HttpResponseForbidden("You don't have permission to access this view")
191-
192-
if not group.charter:
193-
group.charter = get_or_create_initial_charter(group, group_type)
194-
group.save()
195-
196-
return redirect('charter_submit', name=group.charter.name, option="initcharter")
197-
198147
@login_required
199148
def edit(request, group_type=None, acronym=None, action="edit"):
200149
"""Edit or create a group, notifying parties as
@@ -241,9 +190,6 @@ def edit(request, group_type=None, acronym=None, action="edit"):
241190
save_group_in_history(group)
242191

243192

244-
if action == "charter" and not group.charter: # make sure we have a charter
245-
group.charter = get_or_create_initial_charter(group, group_type)
246-
247193
changes = []
248194

249195
def desc(attr, new, old):
@@ -323,7 +269,7 @@ def diff(attr, name):
323269
group.save()
324270

325271
if action=="charter":
326-
return redirect('charter_submit', name=group.charter.name, option="initcharter")
272+
return redirect('charter_submit', name=charter_name_for_group(group), option="initcharter")
327273

328274
return HttpResponseRedirect(group.about_url())
329275
else: # form.is_valid()

ietf/group/info.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from ietf.doc.views_search import SearchForm, retrieve_search_results, get_doc_is_tracked
5151
from ietf.doc.models import Document, State, DocAlias, RelatedDocument
5252
from ietf.doc.utils import get_chartering_type
53+
from ietf.doc.utils_charter import charter_name_for_group
5354
from ietf.doc.templatetags.ietf_filters import clean_whitespace
5455
from ietf.group.models import Group, Role, ChangeStateGroupEvent
5556
from ietf.name.models import GroupTypeName
@@ -471,13 +472,17 @@ def group_about(request, acronym, group_type=None):
471472
requested_close = group.state_id != "conclude" and e and e.type == "requested_close"
472473

473474
can_manage = can_manage_group_type(request.user, group.type_id)
475+
charter_submit_url = ""
476+
if group.features.has_chartering_process:
477+
charter_submit_url = urlreverse("charter_submit", kwargs={ "name": charter_name_for_group(group) })
474478

475479
return render(request, 'group/group_about.html',
476480
construct_group_menu_context(request, group, "charter" if group.features.has_chartering_process else "about", group_type, {
477481
"milestones_in_review": group.groupmilestone_set.filter(state="review"),
478482
"milestone_reviewer": milestone_reviewer_for_group_type(group_type),
479483
"requested_close": requested_close,
480484
"can_manage": can_manage,
485+
"charter_submit_url": charter_submit_url
481486
}))
482487

483488

ietf/group/tests_info.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from django.core.urlresolvers import NoReverseMatch
1414

1515
from ietf.doc.models import Document, DocAlias, DocEvent, State
16+
from ietf.doc.utils_charter import charter_name_for_group
1617
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions, MilestoneGroupEvent
1718
from ietf.group.utils import save_group_in_history
1819
from ietf.name.models import DocTagName, GroupStateName, GroupTypeName
@@ -377,8 +378,7 @@ def test_create(self):
377378
self.assertEqual(len(Group.objects.filter(type="wg")), num_wgs + 1)
378379
group = Group.objects.get(acronym="testwg")
379380
self.assertEqual(group.name, "Testing WG")
380-
self.assertEqual(group.charter.name, "charter-ietf-testwg")
381-
self.assertEqual(group.charter.rev, "00-00")
381+
self.assertEqual(charter_name_for_group(group), "charter-ietf-testwg")
382382

383383
def test_create_rg(self):
384384

@@ -404,9 +404,7 @@ def test_create_rg(self):
404404
self.assertEqual(len(Group.objects.filter(type="rg")), num_rgs + 1)
405405
group = Group.objects.get(acronym="testrg")
406406
self.assertEqual(group.name, "Testing RG")
407-
self.assertEqual(group.charter.name, "charter-irtf-testrg")
408-
self.assertEqual(group.charter.rev, "00-00")
409-
self.assertEqual(group.parent.acronym,'irtf')
407+
self.assertEqual(charter_name_for_group(group), "charter-irtf-testrg")
410408

411409
def test_create_based_on_existing_bof(self):
412410
make_test_data()
@@ -513,19 +511,6 @@ def test_edit_info(self):
513511
self.assertEqual(group.groupurl_set.all()[0].name, "MARS site")
514512
self.assertTrue(os.path.exists(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))))
515513

516-
def test_initial_charter(self):
517-
make_test_data()
518-
group = Group.objects.get(acronym="mars")
519-
for url in [ urlreverse('ietf.group.edit.submit_initial_charter', kwargs={'acronym':group.acronym}),
520-
urlreverse('ietf.group.edit.submit_initial_charter', kwargs={'acronym':group.acronym,'group_type':group.type_id}),
521-
]:
522-
login_testing_unauthorized(self, "secretary", url)
523-
r = self.client.get(url,follow=True)
524-
self.assertEqual(r.status_code,200)
525-
self.assertTrue(r.redirect_chain[0][0].endswith(urlreverse('charter_submit',kwargs={'name':group.charter.name,'option':'initcharter'})))
526-
self.client.logout()
527-
528-
529514
def test_conclude(self):
530515
make_test_data()
531516

ietf/group/urls_info_details.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
(r'^history/$','ietf.group.info.history'),
1010
(r'^deps/dot/$', 'ietf.group.info.dependencies_dot'),
1111
(r'^deps/pdf/$', 'ietf.group.info.dependencies_pdf'),
12-
(r'^init-charter/', 'ietf.group.edit.submit_initial_charter'),
1312
(r'^edit/$', 'ietf.group.edit.edit', {'action': "edit"}, "group_edit"),
1413
(r'^conclude/$', 'ietf.group.edit.conclude'),
1514
(r'^milestones/$', 'ietf.group.milestones.edit_milestones', {'milestone_set': "current"}, "group_edit_milestones"),

ietf/templates/doc/charter/submit.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ <h1>Charter submission<br><small>{{ group.acronym }} {{ group.type.name }}</smal
2222

2323
{% buttons %}
2424
<button type="submit" class="btn btn-primary">Submit</button>
25-
<a class="btn btn-default pull-right" href="{% url "doc_view" name=group.charter.name %}">Back</a>
25+
{% if group.charter %}
26+
<a class="btn btn-default pull-right" href="{% url "doc_view" name=name %}">Back</a>
27+
{% else %}
28+
<a class="btn btn-default pull-right" href="{% url "group_charter" group_type=group.type_id acronym=group.acronym %}">Back</a>
29+
{% endif %}
2630
{% endbuttons %}
2731
</form>
2832

ietf/templates/group/group_about.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
{% else %}
5858
(None)
5959
{% if user|has_role:"Area Director,Secretariat" %}
60-
<a class="btn btn-warning btn-xs" href="{% url "ietf.group.edit.submit_initial_charter" group_type=group.type_id acronym=group.acronym %}">Submit charter</a>
60+
<a class="btn btn-warning btn-xs" href="{{ charter_submit_url }}">Submit charter</a>
6161
{% endif %}
6262
{% endif %}
6363
</td>

0 commit comments

Comments
 (0)