From b73c5358e7c1e8266cff44954b92764b8336af25 Mon Sep 17 00:00:00 2001 From: Martin Qvist <tin@laenke.io> Date: Mon, 22 Aug 2011 19:54:25 +0000 Subject: [PATCH] Added corrections and tests as discussed with Ole. - Legacy-Id: 3349 --- ietf/settings.py | 2 + ietf/templates/wgrecord/add_comment.html | 2 +- ietf/templates/wgrecord/approve_ballot.html | 2 +- ietf/templates/wgrecord/change_state.html | 7 +- ietf/templates/wgrecord/conclude.html | 4 +- ietf/templates/wgrecord/edit_info.html | 2 +- ietf/templates/wgrecord/edit_position.html | 2 +- ietf/templates/wgrecord/record_ballot.html | 6 +- .../wgrecord/record_ballot_list.html | 2 +- .../wgrecord/record_tab_charter.html | 2 +- .../wgrecord/record_tab_history.html | 2 +- .../wgrecord/record_tab_writeup.html | 8 +- .../templates/wgrecord/search_result_row.html | 2 +- ietf/templates/wgrecord/status_columns.html | 4 +- ietf/templates/wgrecord/submit.html | 2 +- ietf/utils/test_data.py | 14 + ietf/wgrecord/feeds.py | 16 +- ietf/wgrecord/mails.py | 2 +- ietf/wgrecord/models.py | 0 .../{rec_ballot_icon.py => wg_ballot_icon.py} | 14 +- ietf/wgrecord/tests.py | 345 +++++++++++++++++- ietf/wgrecord/testurl.list | 55 +++ ietf/wgrecord/urls.py | 14 +- ietf/wgrecord/utils.py | 39 +- ietf/wgrecord/views_ballot.py | 12 +- ietf/wgrecord/views_edit.py | 67 ++-- ietf/wgrecord/views_rec.py | 34 +- ietf/wgrecord/views_search.py | 42 ++- ietf/wgrecord/views_submit.py | 11 +- redesign/doc/models.py | 4 +- static/js/base.js | 6 +- ...rec-change-state.js => wg-change-state.js} | 5 + ...c-edit-position.js => wg-edit-position.js} | 0 33 files changed, 587 insertions(+), 142 deletions(-) create mode 100644 ietf/wgrecord/models.py rename ietf/wgrecord/templatetags/{rec_ballot_icon.py => wg_ballot_icon.py} (88%) create mode 100644 ietf/wgrecord/testurl.list rename static/js/{rec-change-state.js => wg-change-state.js} (84%) rename static/js/{rec-edit-position.js => wg-edit-position.js} (100%) diff --git a/ietf/settings.py b/ietf/settings.py index 95db6b130..4b08d1c57 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -177,6 +177,8 @@ MAX_WG_DELEGATES = 3 INTERNET_DRAFT_PATH = '/a/www/ietf-ftp/internet-drafts/' INTERNET_DRAFT_PDF_PATH = '/a/www/ietf-datatracker/pdf/' RFC_PATH = '/a/www/ietf-ftp/rfc/' +CHARTER_PATH = '/a/www/ietf-ftp/charters/' +CHARTER_TXT_URL = 'http://www.ietf.org/charters/' AGENDA_PATH = '/a/www/www6s/proceedings/' AGENDA_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/agenda/%(wg)s.%(ext)s' MINUTES_PATH_PATTERN = '/a/www/www6s/proceedings/%(meeting)s/minutes/%(wg)s.%(ext)s' diff --git a/ietf/templates/wgrecord/add_comment.html b/ietf/templates/wgrecord/add_comment.html index f52b25ebe..1db80c507 100644 --- a/ietf/templates/wgrecord/add_comment.html +++ b/ietf/templates/wgrecord/add_comment.html @@ -27,7 +27,7 @@ form.add-comment .actions { <tr> <td></td> <td class="actions"> - <a href="{% url record_view name=wg.acronym %}">Back</a> + <a href="{% url wg_view_record name=wg.acronym %}">Back</a> <input type="submit" value="Add comment"/> </td> </tr> diff --git a/ietf/templates/wgrecord/approve_ballot.html b/ietf/templates/wgrecord/approve_ballot.html index eb6d5e0aa..01426c601 100644 --- a/ietf/templates/wgrecord/approve_ballot.html +++ b/ietf/templates/wgrecord/approve_ballot.html @@ -30,7 +30,7 @@ form.approve-ballot .announcement { </div> <div class="actions"> - <a href="{% url record_view name=wg.acronym %}">Back</a> + <a href="{% url wg_view_record name=wg.acronym %}">Back</a> <input type="submit" value="Send out the announcement, close ballot and update revision"/> </div> </form> diff --git a/ietf/templates/wgrecord/change_state.html b/ietf/templates/wgrecord/change_state.html index 59d175aad..5992b32d0 100644 --- a/ietf/templates/wgrecord/change_state.html +++ b/ietf/templates/wgrecord/change_state.html @@ -43,7 +43,7 @@ form.change-state .actions { {% endfor %} <tr> <td colspan="2" class="actions"> - <a href="{% url record_view name=wg.acronym %}">Back</a> + <a href="{% url wg_view_record name=wg.acronym %}">Back</a> <input type="submit" value="Save"/> </td> </tr> @@ -68,10 +68,9 @@ form.change-state .actions { var message = { "infrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to Informal IESG review by {{ login.name }}", "intrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to Internal review by {{ login.name }}. Please place it on the next IESG telechat and inform the IAB.", - "extrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to External review by {{ login.name }}. Please send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: ", - "approved": "The WG {{ wg.name }} ({{ wg.acronym }}) has been approved. Please publish the charter and sent the appropriate announcements." + "extrev": "The WG {{ wg.name }} ({{ wg.acronym }}) has been set to External review by {{ login.name }}. Please send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: " }; </script> -<script type="text/javascript" src="/js/rec-change-state.js"></script> +<script type="text/javascript" src="/js/wg-change-state.js"></script> {% endblock %} diff --git a/ietf/templates/wgrecord/conclude.html b/ietf/templates/wgrecord/conclude.html index 1231ec395..a66911cad 100644 --- a/ietf/templates/wgrecord/conclude.html +++ b/ietf/templates/wgrecord/conclude.html @@ -3,7 +3,7 @@ {% block title %}Request closing of WG {{ wg.acronym }}{% endblock %} {% block morecss %} -#id_comment { +#id_instructions { width: 40em; } @@ -28,7 +28,7 @@ form.conclude .actions { {{ form.as_table }} <tr> <td colspan="2" class="actions"> - <a href="{% url record_view name=wg.acronym %}">Back</a> + <a href="{% url wg_view_record name=wg.acronym %}">Back</a> <input type="submit" value="Send request"/> </td> </tr> diff --git a/ietf/templates/wgrecord/edit_info.html b/ietf/templates/wgrecord/edit_info.html index 260d6b0db..a38849ce6 100644 --- a/ietf/templates/wgrecord/edit_info.html +++ b/ietf/templates/wgrecord/edit_info.html @@ -78,7 +78,7 @@ Create WG record <td></td> <td class="actions"> {% if wg %} - <a href="{% url record_view name=wg.acronym %}">Back</a> + <a href="{% url wg_view_record name=wg.acronym %}">Back</a> {% endif %} <input type="submit" value="Save"/> </td> diff --git a/ietf/templates/wgrecord/edit_position.html b/ietf/templates/wgrecord/edit_position.html index 59608f38a..64e00439c 100644 --- a/ietf/templates/wgrecord/edit_position.html +++ b/ietf/templates/wgrecord/edit_position.html @@ -71,5 +71,5 @@ form.position-form .comment { {% endblock %} {% block content_end %} -<script type="text/javascript" src="/js/rec-edit-position.js"></script> +<script type="text/javascript" src="/js/wg-edit-position.js"></script> {% endblock %} diff --git a/ietf/templates/wgrecord/record_ballot.html b/ietf/templates/wgrecord/record_ballot.html index 095ea36c4..5a32b29ba 100644 --- a/ietf/templates/wgrecord/record_ballot.html +++ b/ietf/templates/wgrecord/record_ballot.html @@ -6,10 +6,10 @@ Copyright The IETF Trust 2011, All Rights Reserved {% if user|in_group:"Area_Director,Secretariat" %} {% if user|in_group:"Area_Director" %} -<div style="margin-top:8px; margin-bottom:8px;"><span id="rec_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url rec_edit_position name=wg.acronym %}">Edit position</a></span></span></div> +<div style="margin-top:8px; margin-bottom:8px;"><span id="wg_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url wg_edit_position name=wg.acronym %}">Edit position</a></span></span></div> {% endif %} {% if user|in_group:"Secretariat" %} -<div style="margin-top:8px; margin-bottom:8px;"><span id="rec_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url rec_approve_ballot name=wg.acronym %}">Approve ballot</a></span></span></div> +<div style="margin-top:8px; margin-bottom:8px;"><span id="wg_ballot_button" class="yui-button yui-link-button"><span class="first-child"><a href="{% url wg_approve_ballot name=wg.acronym %}">Approve ballot</a></span></span></div> {% endif %} {% endif %} @@ -27,7 +27,7 @@ Copyright The IETF Trust 2011, All Rights Reserved <p><span class="square" style="background:white;"></span><b>No Record</b><br/> {% for p in info.pos_no_record %} -<a{% if user|in_group:"Secretariat" %} href="{% url rec_edit_position name=wg.acronym %}?ad={{ p.id }}" title="Click to edit the position of {{ p.name }}"{% endif %}>{{p.name}}</a><br/> +<a{% if user|in_group:"Secretariat" %} href="{% url wg_edit_position name=wg.acronym %}?ad={{ p.id }}" title="Click to edit the position of {{ p.name }}"{% endif %}>{{p.name}}{% if user|in_group:"Secretariat" %}</a>{% endif %}<br/> {% empty %} <i>none</i> {% endfor %} diff --git a/ietf/templates/wgrecord/record_ballot_list.html b/ietf/templates/wgrecord/record_ballot_list.html index 72a81c0de..db513edb2 100644 --- a/ietf/templates/wgrecord/record_ballot_list.html +++ b/ietf/templates/wgrecord/record_ballot_list.html @@ -1,6 +1,6 @@ {% load ietf_filters %} {% for p in positions %} -{% if p.is_old_ad %}[{%endif%}<a{% if user|in_group:"Secretariat" %} href="{% url rec_edit_position name=wg.acronym %}?ad={{ p.ad_id }}" title="Click to edit the position of {{ p.ad }}"{% endif %}>{{p.ad}}</a>{% if p.is_old_ad %}]{%endif%}{% if p.comment or p.block_comment %} <a href="#{{p.ad|slugify}}"><img src="/images/comment.png" width="14" height="12" alt="*" border="0"/></a>{% endif %}<br/> +{% if p.is_old_ad %}[{%endif%}<a{% if user|in_group:"Secretariat" %} href="{% url wg_edit_position name=wg.acronym %}?ad={{ p.ad_id }}" title="Click to edit the position of {{ p.ad }}"{% endif %}>{{p.ad}}</a>{% if p.is_old_ad %}]{%endif%}{% if p.comment or p.block_comment %} <a href="#{{p.ad|slugify}}"><img src="/images/comment.png" width="14" height="12" alt="*" border="0"/></a>{% endif %}<br/> {% if p.old_positions %}<span class="was">(was {{p.old_positions|join:", "}})</span><br/>{%endif%} {% empty %} <i>none</i> diff --git a/ietf/templates/wgrecord/record_tab_charter.html b/ietf/templates/wgrecord/record_tab_charter.html index 027743c49..6a7b9230a 100644 --- a/ietf/templates/wgrecord/record_tab_charter.html +++ b/ietf/templates/wgrecord/record_tab_charter.html @@ -7,7 +7,7 @@ Copyright The IETF Trust 2011, All Rights Reserved {% load ietf_filters %} {% block record_revision %} -Snapshots: {% if not snapshot %}<strong>{% else %}<a href="{% url record_view name=wg.acronym %}">{% endif %}current{% if not snapshot %}</strong>{% else %}</a>{% endif %} {% for d in versions reversed %}{% ifnotequal d.rev wg.charter.rev %}{% ifequal snapshot d.rev %}<strong>{% else %}<a href="{% url record_view name=wg.acronym %}{{d.rev}}/">{% endifequal %}{{ d.rev }}{% ifequal snapshot d.rev %}</strong>{% else %}</a>{% endifequal %} {% endifnotequal %}{% endfor %} +Snapshots: {% if not snapshot %}<strong>{% else %}<a href="{% url wg_view_record name=wg.acronym %}">{% endif %}current{% if not snapshot %}</strong>{% else %}</a>{% endif %} {% for d in versions reversed %}{% ifnotequal d.rev wg.charter.rev %}{% ifequal snapshot d.rev %}<strong>{% else %}<a href="{% url wg_view_record name=wg.acronym %}{{d.rev}}/">{% endifequal %}{{ d.rev }}{% ifequal snapshot d.rev %}</strong>{% else %}</a>{% endifequal %} {% endifnotequal %}{% endfor %} {% endblock %} {% block record_metatable %} diff --git a/ietf/templates/wgrecord/record_tab_history.html b/ietf/templates/wgrecord/record_tab_history.html index 638ff50a5..f681ad914 100644 --- a/ietf/templates/wgrecord/record_tab_history.html +++ b/ietf/templates/wgrecord/record_tab_history.html @@ -10,7 +10,7 @@ Copyright The IETF Trust 2011, All Rights Reserved <h2 style="margin-top:1em;">WG History</h2> {% if user|in_group:"Area_Director,Secretariat" %} <div style="margin-bottom:8px" id="history_actions"> - <span id="rec_add_comment_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url rec_add_comment name=wg.acronym %}">Add comment</a></span></span> + <span id="wg_add_comment_button" class="yui-button yui-link-button" style="margin-left:2px;"><span class="first-child"><a href="{% url wg_add_comment name=wg.acronym %}">Add comment</a></span></span> </div> {% endif %} diff --git a/ietf/templates/wgrecord/record_tab_writeup.html b/ietf/templates/wgrecord/record_tab_writeup.html index 47ec5e61b..f080021d1 100644 --- a/ietf/templates/wgrecord/record_tab_writeup.html +++ b/ietf/templates/wgrecord/record_tab_writeup.html @@ -10,8 +10,8 @@ Copyright The IETF Trust 2011, All Rights Reserved {% if user|in_group:"Area_Director,Secretariat" %} <div style="background:#E0E0FF"> <p align=right> -<span id="rec_edit_announce_button" class="yui-button yui-link-button"><span class="first-child"> -<a href="{% url rec_announcement_text name=wg.acronym ann="review" %}">Edit WG Review Announcement</a> +<span id="wg_edit_announce_button" class="yui-button yui-link-button"><span class="first-child"> +<a href="{% url wg_announcement_text name=wg.acronym ann="review" %}">Edit WG Review Announcement</a> </span></span> </p> {% endif %} @@ -24,8 +24,8 @@ Copyright The IETF Trust 2011, All Rights Reserved {% if user|in_group:"Area_Director,Secretariat" %} <div style="background:#E0E0FF"> <p align=right> -<span id="rec_edit_announce_button" class="yui-button yui-link-button"><span class="first-child"> -<a href="{% url rec_announcement_text name=wg.acronym ann="action" %}">Edit WG Action Announcement</a> +<span id="wg_edit_announce_button" class="yui-button yui-link-button"><span class="first-child"> +<a href="{% url wg_announcement_text name=wg.acronym ann="action" %}">Edit WG Action Announcement</a> </span></span> </p> {% endif %} diff --git a/ietf/templates/wgrecord/search_result_row.html b/ietf/templates/wgrecord/search_result_row.html index 18a109661..695940dba 100644 --- a/ietf/templates/wgrecord/search_result_row.html +++ b/ietf/templates/wgrecord/search_result_row.html @@ -5,7 +5,7 @@ Copyright The IETF Trust 2011, All Rights Reserved {% load ietf_filters %} <tr class="{% cycle oddrow,evenrow %}"> <td class="acronym"> -<a href="{% url record_view name=wg.acronym %}">{{ wg.acronym|safe }}</a> +<a href="{% url wg_view_record name=wg.acronym %}">{{ wg.acronym|safe }}</a> </td> <td class="title">{{ wg.name }}</td> {% include "wgrecord/date_column.html" %} diff --git a/ietf/templates/wgrecord/status_columns.html b/ietf/templates/wgrecord/status_columns.html index cb735d174..84e8d3095 100644 --- a/ietf/templates/wgrecord/status_columns.html +++ b/ietf/templates/wgrecord/status_columns.html @@ -1,7 +1,7 @@ {% comment %} Copyright The IETF Trust 2011, All Rights Reserved {% endcomment %} -{% load ietf_filters ietf_streams %}{% load rec_ballot_icon %} +{% load ietf_filters ietf_streams %}{% load wg_ballot_icon %} <td class="status"> {% if wg.charter %} {{ wg.charter.charter_state|safe }} @@ -13,5 +13,5 @@ Copyright The IETF Trust 2011, All Rights Reserved {% block extra_status %}{% endblock %} </td> <td class="ballot"> -{% ifequal wg.charter.charter_state_id "iesgrev" %}{% rec_ballot_icon wg.acronym %}{% endifequal %} +{% ifequal wg.charter.charter_state_id "iesgrev" %}{% wg_ballot_icon wg.acronym %}{% endifequal %} </td> diff --git a/ietf/templates/wgrecord/submit.html b/ietf/templates/wgrecord/submit.html index 45206229b..166994c6b 100644 --- a/ietf/templates/wgrecord/submit.html +++ b/ietf/templates/wgrecord/submit.html @@ -40,7 +40,7 @@ Charter submission for {{ wg.acronym }} <tr> <td></td> <td class="actions"> - <a href="{% url record_view name=wg.acronym %}">Back</a> + <a href="{% url wg_view_record name=wg.acronym %}">Back</a> <input type="submit" value="Submit"/> </td> </tr> diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 4e2f5fa41..e825097f2 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -60,6 +60,20 @@ def make_test_data(): group=area, email=email) + # ex-ad + u = User.objects.create(username="exad") + ad = p = Person.objects.create( + name="Exaread Irector", + ascii="Exaread Irector", + user=u) + email = Email.objects.create( + address="exaread@ietf.org", + person=p) + Role.objects.create( + name_id="ex-ad", + group=area, + email=email) + # create a bunch of ads for swarm tests for i in range(1, 10): u = User.objects.create(username="ad%s" % i) diff --git a/ietf/wgrecord/feeds.py b/ietf/wgrecord/feeds.py index ac90f4206..74dbaed07 100644 --- a/ietf/wgrecord/feeds.py +++ b/ietf/wgrecord/feeds.py @@ -28,7 +28,7 @@ class GroupComments(Feed): def link(self, obj): if obj is None: raise FeedDoesNotExist - return reverse('record_view', kwargs={'name': obj.acronym}) + return reverse('wg_view_record', kwargs={'name': obj.acronym}) def description(self, obj): return self.title(obj) @@ -38,13 +38,13 @@ class GroupComments(Feed): for h in history: gh = find_history_active_at(obj, h['date']) if gh: - h['chairs'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="chair")) - h['secr'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="secr")) - h['techadv'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="techadv")) + h['chairs'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="chair")] + h['secr'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="secr")] + h['techadv'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="techadv")] else: - h['chairs'] = map(lambda x: x.email.person.name, obj.role_set.filter(name__slug="chair")) - h['secr'] = map(lambda x: x.email.person.name, obj.role_set.filter(name__slug="secr")) - h['techadv'] = map(lambda x: x.email.person.name, obj.role_set.filter(name__slug="techadv")) + h['chairs'] = [x.email.person.name for x in obj.role_set.filter(name__slug="chair")] + h['secr'] = [x.email.person.name for x in obj.role_set.filter(name__slug="secr")] + h['techadv'] = [x.email.person.name for x in obj.role_set.filter(name__slug="techadv")] dh = find_history_active_at(obj.charter, h['date']) if dh: h['rev'] = dh.rev @@ -59,7 +59,7 @@ class GroupComments(Feed): return history def item_link(self, obj): - return reverse('record_view', kwargs={'name': obj['group'].acronym}) + return reverse('wg_view_record', kwargs={'name': obj['group'].acronym}) def item_pubdate(self, obj): return obj['date'] diff --git a/ietf/wgrecord/mails.py b/ietf/wgrecord/mails.py index 8e36f68e6..03da1318b 100644 --- a/ietf/wgrecord/mails.py +++ b/ietf/wgrecord/mails.py @@ -32,5 +32,5 @@ def email_secretariat(request, wg, type, text): "Regarding WG %s: %s" % (wg.acronym, types[type]), "wgrecord/email_secretariat.txt", dict(text=text, - url=settings.IDTRACKER_BASE_URL + urlreverse('record_view', kwargs=dict(name=wg.acronym)))) + url=settings.IDTRACKER_BASE_URL + urlreverse('wg_view_record', kwargs=dict(name=wg.acronym)))) diff --git a/ietf/wgrecord/models.py b/ietf/wgrecord/models.py new file mode 100644 index 000000000..e69de29bb diff --git a/ietf/wgrecord/templatetags/rec_ballot_icon.py b/ietf/wgrecord/templatetags/wg_ballot_icon.py similarity index 88% rename from ietf/wgrecord/templatetags/rec_ballot_icon.py rename to ietf/wgrecord/templatetags/wg_ballot_icon.py index ab490b721..712e36e4f 100644 --- a/ietf/wgrecord/templatetags/rec_ballot_icon.py +++ b/ietf/wgrecord/templatetags/wg_ballot_icon.py @@ -42,11 +42,7 @@ def render_ballot_icon(context, name): latest_positions = [] for p in active_ads: p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=doc, ad=p).order_by("-time")) - if p_pos == []: - pos = GroupBallotPositionDocEvent(doc=doc, ad=p, by=p) - pos.save() - latest_positions.append(pos) - else: + if p_pos != []: latest_positions.append(p_pos[0]) for p in latest_positions: if not p.pos_id: @@ -66,12 +62,12 @@ def render_ballot_icon(context, name): return render_ballot_icon2(wg.acronym, red,yellow,green,gray,blank, my, adId)+"<!-- adId="+str(adId)+" my="+str(my)+"-->" def render_ballot_icon2(acronym, red,yellow,green,gray,blank, my, adId): - edit_position_url = urlreverse('rec_edit_position', kwargs=dict(name=acronym)) + edit_position_url = urlreverse('wg_edit_position', kwargs=dict(name=acronym)) if adId: - res_cm = ' oncontextmenu="editRecBallot(\''+str(edit_position_url)+'\');return false;"' + res_cm = ' oncontextmenu="editWGBallot(\''+str(edit_position_url)+'\');return false;"' else: res_cm = '' - res = '<table class="ballot_icon" title="IESG Review (click to show more, right-click to edit position)" onclick="showRecBallot(\'' + acronym + '\',\'' + str(edit_position_url) + '\')"'+res_cm+'>' + res = '<table class="ballot_icon" title="IESG Review (click to show more, right-click to edit position)" onclick="showWGBallot(\'' + acronym + '\',\'' + str(edit_position_url) + '\')"'+res_cm+'>' for y in range(3): res = res + "<tr>" for x in range(5): @@ -115,7 +111,7 @@ def do_ballot_icon(parser, token): raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] return BallotIconNode(docName) -register.tag('rec_ballot_icon', do_ballot_icon) +register.tag('wg_ballot_icon', do_ballot_icon) @register.filter def my_position(doc, user): diff --git a/ietf/wgrecord/tests.py b/ietf/wgrecord/tests.py index a646f98d6..7132b1f22 100644 --- a/ietf/wgrecord/tests.py +++ b/ietf/wgrecord/tests.py @@ -2,14 +2,40 @@ import os import unittest +import django.test from django.conf import settings from ietf.utils.test_utils import SimpleUrlTestCase +from ietf.utils.test_runner import mail_outbox +from ietf.utils.test_data import make_test_data +from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, login_testing_unauthorized -class WgRecUrlTestCase(SimpleUrlTestCase): +import datetime +from pyquery import PyQuery +from tempfile import NamedTemporaryFile + +from django.contrib.auth.models import User +from doc.models import * +from group.models import * +from name.models import * +from person.models import * +from name.utils import name + +from utils import * + +class WgUrlTestCase(SimpleUrlTestCase): def testUrls(self): self.doTestUrls(__file__) -class WgRecFileTestCase(unittest.TestCase): + def setUp(self, *args, **kwargs): + super(WgUrlTestCase, self).setUp(*args, **kwargs) + # Make test data (because we use the new schema) + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + approved = name(CharterDocStateName, "approved", "Approved") + +class WgFileTestCase(django.test.TestCase): def testFileExistence(self): print " Testing if WG charter texts exist locally" fpath = os.path.join(settings.CHARTER_PATH, "charter-ietf-example-01.txt") @@ -19,4 +45,317 @@ class WgRecFileTestCase(unittest.TestCase): print "Remember to set CHARTER_PATH in settings_local.py\n" else: print "OK (seem to exist)" - + +class WgStateTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_change_state(self): + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review") + infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review") + intrev=name(CharterDocStateName, slug="intrev", name="Internal review") + extrev=name(CharterDocStateName, slug="extrev", name="External review") + iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review") + approved=name(CharterDocStateName, slug="approved", name="Approved") + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = set_or_create_charter(group) + + charter.charter_state = infrev + charter.save() + + # -- Test change state -- + url = urlreverse('wg_change_state', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + first_state = charter.charter_state + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form select[name=state]')), 1) + + # faulty post + r = self.client.post(url, dict(state="foobarbaz")) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + self.assertEquals(charter.charter_state, first_state) + + # change state + for s in ("intrev", "extrev", "iesgrev", "approved"): + events_before = charter.docevent_set.count() + mailbox_before = len(mail_outbox) + + r = self.client.post(url, dict(state="active", charter_state=s, message="test message")) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) + self.assertEquals(charter.charter_state_id, s) + self.assertEquals(charter.docevent_set.count(), events_before + 1) + self.assertTrue("State changed" in charter.docevent_set.all()[0].desc) + if s == "extrev": + self.assertEquals(len(mail_outbox), mailbox_before + 2) + self.assertTrue("State changed" in mail_outbox[-1]['Subject']) + self.assertTrue("State changed" in mail_outbox[-2]['Subject']) + else: + self.assertEquals(len(mail_outbox), mailbox_before + 1) + if s == "approved": + self.assertTrue("Charter approved" in mail_outbox[-1]['Subject']) + else: + self.assertTrue("State changed" in mail_outbox[-1]['Subject']) + + def test_conclude(self): + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review") + infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review") + intrev=name(CharterDocStateName, slug="intrev", name="Internal review") + extrev=name(CharterDocStateName, slug="extrev", name="External review") + iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review") + approved=name(CharterDocStateName, slug="approved", name="Approved") + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = set_or_create_charter(group) + + charter.charter_state = approved + charter.save() + + # -- Test conclude WG -- + url = urlreverse('wg_conclude', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form textarea[name=instructions]')), 1) + + # faulty post + r = self.client.post(url, dict(instructions="")) # No instructions + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + group = Group.objects.get(acronym="mars") + + # conclusion request + r = self.client.post(url, dict(instructions="Test instructions")) + self.assertEquals(r.status_code, 302) + # The WG remains active until the state is set to conclude via change_state + self.assertEquals(group.state_id, "active") + +class WgInfoTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_create(self): + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review") + infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review") + intrev=name(CharterDocStateName, slug="intrev", name="Internal review") + extrev=name(CharterDocStateName, slug="extrev", name="External review") + iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review") + approved=name(CharterDocStateName, slug="approved", name="Approved") + + # -- Test WG creation -- + url = urlreverse('wg_create') + login_testing_unauthorized(self, "secretary", url) + + num_wgs = len(Group.objects.filter(type="wg")) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form input[name=acronym]')), 1) + + # faulty post + r = self.client.post(url, dict(acronym="foobarbaz")) # No name + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + self.assertEquals(len(Group.objects.filter(type="wg")), num_wgs) + + # creation + r = self.client.post(url, dict(acronym="testwg", name="Testing WG")) + self.assertEquals(r.status_code, 302) + self.assertEquals(len(Group.objects.filter(type="wg")), num_wgs + 1) + group = Group.objects.get(acronym="testwg") + self.assertEquals(group.name, "Testing WG") + # check that a charter was created with the correct name + self.assertEquals(group.charter.name, "charter-ietf-testwg") + # and that it has no revision + self.assertEquals(group.charter.rev, "") + + + def test_edit_info(self): + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review") + infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review") + intrev=name(CharterDocStateName, slug="intrev", name="Internal review") + extrev=name(CharterDocStateName, slug="extrev", name="External review") + iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review") + approved=name(CharterDocStateName, slug="approved", name="Approved") + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = set_or_create_charter(group) + + url = urlreverse('wg_edit_info', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form select[name=parent]')), 1) + self.assertEquals(len(q('form input[name=acronym]')), 1) + + # faulty post + Group.objects.create(name="Collision Test Group", acronym="collide") + r = self.client.post(url, dict(acronym="collide")) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + + # Create old acronym + group.acronym = "oldmars" + group.save() + save_group_in_history(group) + group.acronym = "mars" + group.save() + + # post with warning + r = self.client.post(url, dict(acronym="oldmars")) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertTrue(len(q('form ul.errorlist')) > 0) + + # edit info + r = self.client.post(url, + dict(name="Mars Not Special Interest Group", + acronym="mnsig", + parent=2, + ad=1, + chairs="aread@ietf.org", + secr="aread@ietf.org", + techadv="aread@ietf.org", + list_email="mars@mail", + list_subscribe="subscribe.mars", + list_archive="archive.mars", + urls="http://mars.mars (MARS site)" + )) + self.assertEquals(r.status_code, 302) + + group = Group.objects.get(acronym="mnsig") + self.assertEquals(group.name, "Mars Not Special Interest Group") + self.assertEquals(group.parent_id, 2) + self.assertEquals(group.ad.name, "Aread Irector") + for k in ("chair", "secr", "techadv"): + self.assertEquals(group.role_set.filter(name="chair")[0].email.address, "aread@ietf.org") + self.assertEquals(group.list_email, "mars@mail") + self.assertEquals(group.list_subscribe, "subscribe.mars") + self.assertEquals(group.list_archive, "archive.mars") + self.assertEquals(group.groupurl_set.all()[0].url, "http://mars.mars") + self.assertEquals(group.groupurl_set.all()[0].name, "MARS site") + + def test_edit_telechat_date(self): + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review") + infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review") + intrev=name(CharterDocStateName, slug="intrev", name="Internal review") + extrev=name(CharterDocStateName, slug="extrev", name="External review") + iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review") + approved=name(CharterDocStateName, slug="approved", name="Approved") + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = set_or_create_charter(group) + + url = urlreverse('wg_edit_info', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + from ietf.iesg.models import TelechatDates + + # add to telechat + self.assertTrue(not charter.latest_event(TelechatDocEvent, "scheduled_for_telechat")) + telechat_date = TelechatDates.objects.all()[0].date1.isoformat() + r = self.client.post(url, dict(name=group.name, acronym=group.acronym, telechat_date=telechat_date)) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertTrue(charter.latest_event(TelechatDocEvent, "scheduled_for_telechat")) + self.assertEquals(charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, TelechatDates.objects.all()[0].date1) + + # change telechat + telechat_date = TelechatDates.objects.all()[0].date2.isoformat() + r = self.client.post(url, dict(name=group.name, acronym=group.acronym, telechat_date=telechat_date)) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertEquals(charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, TelechatDates.objects.all()[0].date2) + + # remove from agenda + telechat_date = "" + r = self.client.post(url, dict(name=group.name, acronym=group.acronym, telechat_date=telechat_date)) + self.assertEquals(r.status_code, 302) + + charter = Document.objects.get(name=charter.name) + self.assertTrue(not charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date) + + def test_submit_charter(self): + make_test_data() + # Make sure all relevant names are created + type_charter = name(DocTypeName, "charter", "Charter") + active = name(GroupStateName, "active", "Active") + notrev=name(CharterDocStateName, slug="notrev", name="Not currently under review") + infrev=name(CharterDocStateName, slug="infrev", name="Informal IESG review") + intrev=name(CharterDocStateName, slug="intrev", name="Internal review") + extrev=name(CharterDocStateName, slug="extrev", name="External review") + iesgrev=name(CharterDocStateName, slug="iesgrev", name="IESG review") + approved=name(CharterDocStateName, slug="approved", name="Approved") + + # And make a charter for group + group = Group.objects.get(acronym="mars") + charter = set_or_create_charter(group) + + url = urlreverse('wg_submit', kwargs=dict(name=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form input[name=txt]')), 1) + + prev_rev = charter.rev + + file = NamedTemporaryFile() + file.write("Test content") + file.seek(0) + + r = self.client.post(url, dict(txt=file)) + self.assertEquals(r.status_code, 302) + + file.close() + + charter = Document.objects.get(name="charter-ietf-mars") + self.assertEquals(charter.rev, next_revision(prev_rev)) + self.assertTrue("new_revision" in charter.latest_event().type) + diff --git a/ietf/wgrecord/testurl.list b/ietf/wgrecord/testurl.list new file mode 100644 index 000000000..7d1eb8c91 --- /dev/null +++ b/ietf/wgrecord/testurl.list @@ -0,0 +1,55 @@ +200 / +200 /wgrecord/ + +# In IESG process +200 /wgrecord/in_process/ + +# by AD +# current AD -- needs to be updated at some point +200 /wgrecord/ad/aread.irector/ +# former AD +200 /wgrecord/ad/exaread.irector/ +404 /wgrecord/ad/no.body/ + +# create +302 /wgrecord/create/ + +# WG +200 /wgrecord/mars/ +200 /wgrecord/mars/00-00/ +200 /wgrecord/mars/_ballot.data + +# Edit WG +302 /wgrecord/mars/edit/state/ +302 /wgrecord/mars/edit/info/ +302 /wgrecord/mars/edit/conclude/ +302 /wgrecord/mars/edit/addcomment/ +302 /wgrecord/mars/edit/action/ +302 /wgrecord/mars/edit/review/ + +# ballots +302 /wgrecord/mars/edit/position/ +302 /wgrecord/mars/edit/sendballotcomment/ +302 /wgrecord/mars/edit/approveballot/ + +# submission of charters +302 /wgrecord/mars/submit/ + +# search +200 /wgrecord/search/ +302 /wgrecord/search/?name=martian +200 /wgrecord/search/?name=something +200 /wgrecord/search/?name=something&by=acronym&acronym=some +200 /wgrecord/search/?name=something&by=state&state=active&charter_state= +200 /wgrecord/search/?name=something&by=state&state=&charter_state=approved +200 /wgrecord/search/?name=something&by=ad&ad=1 +200 /wgrecord/search/?name=something&by=area&area=2 +200 /wgrecord/search/?name=something&by=anyfield&anyfield=something +200 /wgrecord/search/?name=something&by=eacronym&eacronym=someold + +# searchPerson (ajax) +200 /wgrecord/searchPerson/ + +# + + diff --git a/ietf/wgrecord/urls.py b/ietf/wgrecord/urls.py index 7be41f3a2..d42a72dcd 100644 --- a/ietf/wgrecord/urls.py +++ b/ietf/wgrecord/urls.py @@ -14,16 +14,16 @@ urlpatterns += patterns('', (r'^searchPerson/$', views_search.search_person), url(r'^ad/(?P<name>[A-Za-z0-9.-]+)/$', views_search.by_ad, name="wg_search_by_ad"), url(r'^in_process/$', views_search.in_process, name="wg_search_in_process"), - url(r'^(?P<name>[A-Za-z0-9._-]+)/((?P<rev>[0-9][0-9](-[0-9][0-9])?)/)?((?P<tab>ballot|writeup|history)/)?$', views_rec.record_main, name="record_view"), - (r'^(?P<name>[A-Za-z0-9._-]+)/_ballot.data$', views_rec.record_ballot), + url(r'^(?P<name>[A-Za-z0-9._-]+)/((?P<rev>[0-9][0-9](-[0-9][0-9])?)/)?((?P<tab>ballot|writeup|history)/)?$', views_rec.wg_main, name="wg_view_record"), + (r'^(?P<name>[A-Za-z0-9._-]+)/_ballot.data$', views_rec.wg_ballot), url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/state/$', views_edit.change_state, name='wg_change_state'), url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/info/$', views_edit.edit_info, name='wg_edit_info'), url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/conclude/$', views_edit.conclude, name='wg_conclude'), - url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/addcomment/$', views_edit.add_comment, name='rec_add_comment'), - url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/(?P<ann>action|review)/$', views_ballot.announcement_text, name='rec_announcement_text'), - url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/position/$', views_ballot.edit_position, name='rec_edit_position'), - url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/sendballotcomment/$', views_ballot.send_ballot_comment, name='rec_send_ballot_comment'), - url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='rec_approve_ballot'), + url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/addcomment/$', views_edit.add_comment, name='wg_add_comment'), + url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/(?P<ann>action|review)/$', views_ballot.announcement_text, name='wg_announcement_text'), + url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/position/$', views_ballot.edit_position, name='wg_edit_position'), + url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/sendballotcomment/$', views_ballot.send_ballot_comment, name='wg_send_ballot_comment'), + url(r'^(?P<name>[A-Za-z0-9._-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='wg_approve_ballot'), url(r'^(?P<name>[A-Za-z0-9._-]+)/submit/$', views_submit.submit, name='wg_submit'), ) diff --git a/ietf/wgrecord/utils.py b/ietf/wgrecord/utils.py index d64f664a3..385192a43 100644 --- a/ietf/wgrecord/utils.py +++ b/ietf/wgrecord/utils.py @@ -1,9 +1,28 @@ from django.conf import settings import re -from redesign.group.models import GroupEvent +from datetime import datetime +from group.models import GroupEvent +from doc.models import Document from ietf.utils.history import find_history_active_at +def set_or_create_charter(wg): + try: + charter = Document.objects.get(name="charter-ietf-" + wg.acronym) + except Document.DoesNotExist: + charter = Document.objects.create( + name="charter-ietf-%s" % wg.acronym, + time=datetime.now(), + type_id="charter", + title=wg.name, + group=wg, + abstract=wg.name, + rev="", + ) + wg.charter = charter + wg.save() + return charter + def add_wg_comment(request, wg, text, ballot=None): if request: login = request.user.get_profile() @@ -29,6 +48,24 @@ def log_state_changed(request, doc, by, prev_state, note=''): e.save() return e +def log_group_state_changed(request, wg, by, note=''): + from group.models import GroupEvent + + e = GroupEvent(group=wg, by=by) + if wg.state_id == "proposed": + e.type = "proposed" + elif wg.state_id == "active": + e.type = "started" + elif wg.state_id == "conclude": + e.type = "concluded" + e.desc = u"%s group" % e.type.capitalize() + + if note: + e.desc += "<br>%s" % note + + e.save() + return e + def log_info_changed(request, wg, by, type=None, note=''): from group.models import GroupEvent diff --git a/ietf/wgrecord/views_ballot.py b/ietf/wgrecord/views_ballot.py index 21f936605..e03f05a12 100644 --- a/ietf/wgrecord/views_ballot.py +++ b/ietf/wgrecord/views_ballot.py @@ -96,7 +96,7 @@ def edit_position(request, name): except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: - return redirect('rec_edit_position', name=wglist[0].group.acronym) + return redirect('wg_edit_position', name=wglist[0].group.acronym) else: raise Http404 @@ -193,7 +193,7 @@ def edit_position(request, name): qstr = "?return_to_url=%s" % return_to_url if request.GET.get('ad'): qstr += "&ad=%s" % request.GET.get('ad') - return HttpResponseRedirect(urlreverse("rec_send_ballot_comment", kwargs=dict(name=wg.acronym)) + qstr) + return HttpResponseRedirect(urlreverse("wg_send_ballot_comment", kwargs=dict(name=wg.acronym)) + qstr) else: return HttpResponseRedirect(return_to_url) else: @@ -226,7 +226,7 @@ def send_ballot_comment(request, name): except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: - return redirect('rec_send_ballot_comment', name=wglist[0].group.acronym) + return redirect('wg_send_ballot_comment', name=wglist[0].group.acronym) else: raise Http404 @@ -315,7 +315,7 @@ def announcement_text(request, name, ann): except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: - return redirect('rec_announcement_text', name=wglist[0].group.acronym) + return redirect('wg_announcement_text', name=wglist[0].group.acronym) else: raise Http404 @@ -349,7 +349,7 @@ def announcement_text(request, name, ann): doc.time = e.time doc.save() - return redirect('record_view', name=wg.acronym) + return redirect('wg_view_record', name=wg.acronym) return render_to_response('wgrecord/announcement_text.html', dict(doc=doc, announcement=ann, @@ -366,7 +366,7 @@ def approve_ballot(request, name): except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: - return redirect('rec_approve_ballot', name=wglist[0].group.acronym) + return redirect('wg_approve_ballot', name=wglist[0].group.acronym) else: raise Http404 diff --git a/ietf/wgrecord/views_edit.py b/ietf/wgrecord/views_edit.py index 2f8112e0b..017bea759 100644 --- a/ietf/wgrecord/views_edit.py +++ b/ietf/wgrecord/views_edit.py @@ -9,7 +9,7 @@ from django import forms from django.forms.util import ErrorList from django.core.exceptions import ObjectDoesNotExist -from utils import log_state_changed, log_info_changed, update_telechat +from utils import log_state_changed, log_group_state_changed, log_info_changed, update_telechat, add_wg_comment, set_or_create_charter from mails import email_secretariat from ietf.ietfauth.decorators import group_required from ietf.iesg.models import TelechatDates @@ -19,7 +19,6 @@ from name.models import CharterDocStateName, GroupStateName, GroupTypeName, DocT from person.models import Person, Email from group.models import Group, GroupEvent, GroupHistory, GroupURL, Role, RoleHistory, save_group_in_history -from utils import add_wg_comment from views_search import json_emails class ChangeStateForm(forms.Form): @@ -45,14 +44,14 @@ def change_state(request, name): # Get WG by acronym, redirecting if there's a newer acronym try: wg = Group.objects.get(acronym=name) + charter = set_or_create_charter(wg) except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: return redirect('wg_change_state', name=wglist[0].group.acronym) else: raise Http404 - # Get charter - charter = wg.charter if wg.charter else None + initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review") login = request.user.get_profile() @@ -60,7 +59,7 @@ def change_state(request, name): if request.method == 'POST': form = ChangeStateForm(request.POST) if form.is_valid(): - if initial_review and form.cleaned_data['charter_state'].slug != "infrev" and initial_review.expires > datetime.now() and not form.cleaned_data['confirm_state']: + if charter.charter_state_id == "infrev" and initial_review and form.cleaned_data['charter_state'].slug != "infrev" and initial_review.expires > datetime.now() and not form.cleaned_data['confirm_state']: form._errors['charter_state'] = "warning" else: state = form.cleaned_data['state'] @@ -95,6 +94,8 @@ def change_state(request, name): prev = wg.state wg.state = state + + ge = log_group_state_changed(request, wg, login, comment) wg.save() if change: @@ -115,7 +116,7 @@ def change_state(request, name): e.desc = "IESG process started in state <b>%s</b>" % charter.charter_state.name e.save() - if form.cleaned_data["initial_time"] != 0: + if form.cleaned_data["charter_state"].slug == "infrev" and form.cleaned_data["initial_time"] and form.cleaned_data["initial_time"] != 0: e = InitialReviewDocEvent() e.type = "initial_review" e.by = login @@ -124,7 +125,7 @@ def change_state(request, name): e.desc = "Initial review time expires %s" % e.expires.strftime("%Y-%m-%d") e.save() - return redirect('record_view', name=wg.acronym) + return redirect('wg_view_record', name=wg.acronym) else: if wg.state_id != "proposed": states = CharterDocStateName.objects.filter(slug__in=["infrev", "intrev", "extrev", "iesgrev", "approved"]) @@ -212,6 +213,7 @@ def edit_info(request, name=None): if request.path_info == reverse('wg_edit_info', kwargs={'name': name}): # Editing. Get group wg = get_object_or_404(Group, acronym=name) + charter = set_or_create_charter(wg) new_wg = False elif request.path_info == reverse('wg_create'): wg = None @@ -219,14 +221,15 @@ def edit_info(request, name=None): login = request.user.get_profile() - if wg and wg.charter: - e = wg.charter.latest_event(TelechatDocEvent, type="scheduled_for_telechat") + if not new_wg: + e = charter.latest_event(TelechatDocEvent, type="scheduled_for_telechat") initial_telechat_date = e.telechat_date if e else None else: initial_telechat_date = None if request.method == 'POST': form = EditInfoForm(request.POST, initial=dict(telechat_date=initial_telechat_date)) + if form.is_valid(): if (new_wg or form.cleaned_data['acronym'] != wg.acronym) and not_valid_acronym(form.cleaned_data['acronym']) and not form.cleaned_data['confirm_acronym']: try: @@ -254,17 +257,9 @@ def edit_info(request, name=None): e.save() if not wg.charter: # Create adjoined charter - try: - charter = Document.objects.get(name="charter-ietf-"+r["acronym"]) - except ObjectDoesNotExist: - charter = Document(type=DocTypeName.objects.get(name="Charter"), - title=r["name"], - abstract=r["name"], - name="charter-ietf-" + r["acronym"], - ) - charter.charter_state = CharterDocStateName.objects.get(slug="infrev") - charter.group = wg - charter.save() + charter = set_or_create_charter(wg) + charter.charter_state = CharterDocStateName.objects.get(slug="infrev") + charter.save() e = DocEvent(doc=charter, type="started_iesg_process") e.time = datetime.now() @@ -340,7 +335,7 @@ def edit_info(request, name=None): attr = attr_role[0] rname = attr_role[1] new = get_sorted_string(attr, ",") - old = map(lambda x: x.email.address, wg.role_set.filter(name__name=rname).order_by('email__address')) + old = [x.email.address for x in wg.role_set.filter(name__name=rname).order_by('email__address')] if new != old: # Remove old roles and save them in history for role in wg.role_set.filter(name__name=rname): @@ -354,7 +349,7 @@ def edit_info(request, name=None): # update urls new_urls = get_sorted_string('urls', '\n') - old_urls = map(lambda x: x.url + " (" + x.name + ")", wg.groupurl_set.order_by('url')) + old_urls = [x.url + " (" + x.name + ")" for x in wg.groupurl_set.order_by('url')] if new_urls != old_urls: # Remove old urls for u in wg.groupurl_set.all(): @@ -377,21 +372,21 @@ def edit_info(request, name=None): if new_wg: return redirect('wg_change_state', name=wg.acronym) else: - return redirect('record_view', name=wg.acronym) + return redirect('wg_view_record', name=wg.acronym) else: - if wg: - init = dict(name=wg.name if wg.name else None, + if not new_wg: + init = dict(name=wg.name, acronym=wg.acronym, - chairs=json_emails(map(lambda x: x.email, wg.role_set.filter(name="Chair"))), - secretaries=json_emails(map(lambda x: x.email, wg.role_set.filter(name="Secr"))), - techadv=json_emails(map(lambda x: x.email, wg.role_set.filter(name="Techadv"))), + chairs=json_emails([x.email for x in wg.role_set.filter(name="Chair")]), + secretaries=json_emails([x.email for x in wg.role_set.filter(name="Secr")]), + techadv=json_emails([x.email for x in wg.role_set.filter(name="Techadv")]), charter=wg.charter.name if wg.charter else None, ad=wg.ad.id if wg.ad else None, parent=wg.parent.id if wg.parent else None, list_email=wg.list_email if wg.list_email else None, list_subscribe=wg.list_subscribe if wg.list_subscribe else None, list_archive=wg.list_archive if wg.list_archive else None, - urls=string.join(map(lambda x: x.url + " (" + x.name + ")", wg.groupurl_set.all()), "\n"), + urls=string.join([x.url + " (" + x.name + ")" for x in wg.groupurl_set.all()], "\n"), comments=wg.comments if wg.comments else None, telechat_date=initial_telechat_date, ) @@ -408,7 +403,7 @@ def edit_info(request, name=None): context_instance=RequestContext(request)) class ConcludeForm(forms.Form): - comment = forms.CharField(widget=forms.Textarea, label="Instructions", required=False) + instructions = forms.CharField(widget=forms.Textarea, required=True) @group_required('Area_Director','Secretariat') def conclude(request, name): @@ -427,15 +422,11 @@ def conclude(request, name): if request.method == 'POST': form = ConcludeForm(request.POST) if form.is_valid(): - comment = form.cleaned_data['comment'] - save_group_in_history(wg) - - wg.state = GroupStateName.objects.get(name="Concluded") - wg.save() + instructions = form.cleaned_data['instructions'] - email_secretariat(request, wg, "conclude", comment) + email_secretariat(request, wg, "conclude", instructions) - return redirect('record_view', name=wg.acronym) + return redirect('wg_view_record', name=wg.acronym) else: form = ConcludeForm() @@ -463,7 +454,7 @@ def add_comment(request, name): #email_owner(request, doc, doc.ad, login, # "A new comment added by %s" % login.name) - return redirect('record_view', name=wg.acronym) + return redirect('wg_view_record', name=wg.acronym) else: form = AddCommentForm() diff --git a/ietf/wgrecord/views_rec.py b/ietf/wgrecord/views_rec.py index f260371db..a40d1d5a1 100644 --- a/ietf/wgrecord/views_rec.py +++ b/ietf/wgrecord/views_rec.py @@ -35,7 +35,7 @@ def _get_html(key, filename): return content @decorator_from_middleware(GZipMiddleware) -def record_main(request, name, rev, tab): +def wg_main(request, name, rev, tab): if tab is None: tab = "charter" try: @@ -43,10 +43,13 @@ def record_main(request, name, rev, tab): except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: - return redirect('record_view', name=wglist[0].group.acronym) + return redirect('wg_view_record', name=wglist[0].group.acronym) else: raise Http404 + if not wg.charter: + create_empty_charter(wg) + if rev != None: charter = get_charter_for_revision(wg.charter, rev) gh = get_group_for_revision(wg, rev) @@ -56,23 +59,23 @@ def record_main(request, name, rev, tab): info = {} - info['prev_acronyms'] = list(set(map(lambda x: x.acronym, wg.history_set.exclude(acronym=wg.acronym)))) - prev_list_email = list(set(map(lambda x: x.list_email, wg.history_set.exclude(list_email=wg.list_email)))) + info['prev_acronyms'] = list(set([x.acronym for x in wg.history_set.exclude(acronym=wg.acronym)])) + prev_list_email = list(set([x.list_email for x in wg.history_set.exclude(list_email=wg.list_email)])) if prev_list_email != [u'']: info['prev_list_email'] = prev_list_email - prev_list_subscribe = list(set(map(lambda x: x.list_email, wg.history_set.exclude(list_subscribe=wg.list_subscribe)))) + prev_list_subscribe = list(set([x.list_email for x in wg.history_set.exclude(list_subscribe=wg.list_subscribe)])) if prev_list_subscribe != [u'']: info['prev_list_subscribe'] = prev_list_subscribe - prev_list_archive = list(set(map(lambda x: x.list_archive, wg.history_set.exclude(list_archive=wg.list_archive)))) + prev_list_archive = list(set([x.list_archive for x in wg.history_set.exclude(list_archive=wg.list_archive)])) if prev_list_archive != [u'']: info['prev_list_archive'] = prev_list_archive - info['chairs'] = map(lambda x: x.email.person.name, wg.role_set.filter(name__slug="chair")) + info['chairs'] = [x.email.person.name for x in wg.role_set.filter(name__slug="chair")] if hasattr(gh, 'rolehistory_set'): - info['history_chairs'] = map(lambda x: x.email.person.name, gh.rolehistory_set.filter(name__slug="chair")) + info['history_chairs'] = [x.email.person.name for x in gh.rolehistory_set.filter(name__slug="chair")] else: - info['history_chairs'] = map(lambda x: x.email.person.name, gh.role_set.filter(name__slug="chair")) - info['secr'] = map(lambda x: x.email.person.name, wg.role_set.filter(name__slug="secr")) - info['techadv'] = map(lambda x: x.email.person.name, wg.role_set.filter(name__slug="techadv")) + info['history_chairs'] = [x.email.person.name for x in gh.role_set.filter(name__slug="chair")] + info['secr'] = [x.email.person.name for x in wg.role_set.filter(name__slug="secr")] + info['techadv'] = [x.email.person.name for x in wg.role_set.filter(name__slug="techadv")] if charter: content = _get_html( @@ -109,7 +112,8 @@ def record_main(request, name, rev, tab): versions = _get_versions(wg.charter) # Important: wg.charter not charter history = _get_history(wg) - info['last_update'] = history[0]['date'] + if history: + info['last_update'] = history[0]['date'] charter_text_url = charter.get_txt_url() @@ -181,13 +185,13 @@ def _get_versions(charter, include_replaced=True): ov.append({"name": charter.name, "rev": charter.rev, "date":charter.time}) return ov -def record_ballot(request, name): +def wg_ballot(request, name): try: wg = Group.objects.get(acronym=name) except ObjectDoesNotExist: wglist = GroupHistory.objects.filter(acronym=name) if wglist: - return redirect('record_view', name=wglist[0].group.acronym) + return redirect('wg_view_record', name=wglist[0].group.acronym) else: raise Http404 @@ -203,7 +207,7 @@ def record_ballot(request, name): latest_positions = [] no_record = [] for p in active_ads: - p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=doc, ad=p).order_by("-time")) + p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=wg.charter, ad=p).order_by("-time")) if p_pos != []: latest_positions.append(p_pos[0]) else: diff --git a/ietf/wgrecord/views_search.py b/ietf/wgrecord/views_search.py index 99ef1a3c0..95ea7e4e7 100644 --- a/ietf/wgrecord/views_search.py +++ b/ietf/wgrecord/views_search.py @@ -101,27 +101,31 @@ def search_query(query_original, sort_by=None): elif by == "area": results = results.filter(parent=query["area"]) elif by == "anyfield": - q_objs = [] - q_objs.append(Q(name__icontains=query['anyfield'])) - q_objs.append(Q(acronym__icontains=query['anyfield'])) - q_objs.append(Q(state__name__icontains=query['anyfield'])) - q_objs.append(Q(charter__charter_state__name__icontains=query['anyfield'])) - q_objs.append(Q(ad__name__icontains=query['anyfield'])) - q_objs.append(Q(parent__name__icontains=query['anyfield'])) - q_objs.append(Q(history_set__acronym__icontains=query['anyfield'])) - results = list(results.filter(reduce(lambda x, y: x | y, q_objs))) + q_objs = Q() + q_objs |= Q(acronym__icontains=query['anyfield']) + q_objs |= Q(state__name__icontains=query['anyfield']) + q_objs |= Q(charter__charter_state__name__icontains=query['anyfield']) + q_objs |= Q(ad__name__icontains=query['anyfield']) + q_objs |= Q(parent__name__icontains=query['anyfield']) + q_objs |= Q(history_set__acronym__icontains=query['anyfield']) + results = list(results.filter(*q_objs)) # Search charter texts m = re.compile(query['anyfield'], re.IGNORECASE) - for g in Group.objects.filter(type="wg"): + if query['name']: + file_set = Group.objects.filter(type="wg", name__icontains=query["name"]) + else: + file_set = Group.objects.filter(type="wg") + for g in file_set: charter = g.charter - try: - file = open(os.path.join(charter.get_file_path(), charter.name+"-"+charter.rev+".txt")) - for line in file: - if m.search(line): - results.append(g) - break - except IOError: - pass # Pass silently for files not found + if charter: + try: + file = open(os.path.join(charter.get_file_path(), charter.name+"-"+charter.rev+".txt")) + for line in file: + if m.search(line): + results.append(g) + break + except IOError: + pass # Pass silently for files not found elif by == "eacronym": results = results.filter(history_set__acronym__icontains=query["eacronym"]).distinct() @@ -192,7 +196,7 @@ def search_results(request): return render_to_response('wgrecord/search_results.html', {'recs':results, 'meta':meta}, context_instance=RequestContext(request)) elif len(results)==1: wg = results[0] - return redirect('record_view', name=wg.acronym) + return redirect('wg_view_record', name=wg.acronym) else: return render_to_response('wgrecord/search_main.html', {'form':form, 'recs':results,'meta':meta}, context_instance=RequestContext(request)) diff --git a/ietf/wgrecord/views_submit.py b/ietf/wgrecord/views_submit.py index 19c94df6c..b8ae2c566 100644 --- a/ietf/wgrecord/views_submit.py +++ b/ietf/wgrecord/views_submit.py @@ -8,20 +8,18 @@ from django.shortcuts import render_to_response, get_object_or_404, redirect from django.core.urlresolvers import reverse from django.template import RequestContext +from ietf.ietfauth.decorators import group_required from django.core.exceptions import ObjectDoesNotExist from group.models import save_group_in_history from doc.models import Document, DocHistory, DocEvent, save_document_in_history from redesign.group.models import Group from django.conf import settings -from utils import next_revision +from utils import next_revision, set_or_create_charter class UploadForm(forms.Form): txt = forms.FileField(label=".txt format", required=True) - def __init__(self, *args, **kwargs): - super(UploadForm, self).__init__(*args, **kwargs) - def save(self, wg): for ext in ['txt']: fd = self.cleaned_data[ext] @@ -33,6 +31,7 @@ class UploadForm(forms.Form): destination.write(chunk) destination.close() +@group_required('Area_Director','Secretariat') def submit(request, name): # Get WG by acronym, redirecting if there's a newer acronym try: @@ -44,7 +43,7 @@ def submit(request, name): else: raise Http404 # Get charter - charter = wg.charter if wg.charter else None + charter = set_or_create_charter(wg) login = request.user.get_profile() @@ -70,7 +69,7 @@ def submit(request, name): charter.time = datetime.now() charter.save() - return HttpResponseRedirect(reverse('record_view', kwargs={'name': wg.acronym})) + return HttpResponseRedirect(reverse('wg_view_record', kwargs={'name': wg.acronym})) else: form = UploadForm() return render_to_response('wgrecord/submit.html', diff --git a/redesign/doc/models.py b/redesign/doc/models.py index c02b128a1..19028e00d 100644 --- a/redesign/doc/models.py +++ b/redesign/doc/models.py @@ -54,7 +54,7 @@ class DocumentInfo(models.Model): raise NotImplemented def get_txt_url(self): - if self.type.slug == "charter": + if self.type_id == "charter": return "http://www.ietf.org/charters/" else: raise NotImplemented @@ -97,7 +97,7 @@ class Document(DocumentInfo): def get_absolute_url(self): name = self.name - if self.type.slug == "charter": + if self.type_id == "charter": return urlreverse('record_view', kwargs={ 'name': self.group.acronym }) else: if self.state == "rfc": diff --git a/static/js/base.js b/static/js/base.js index 2fbb36d8c..d0e6b38c2 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -75,7 +75,7 @@ function editBallot(editPositionUrl) { window.open(editPositionUrl); } -function showRecBallot(acronym, editPositionUrl) { +function showWGBallot(acronym, editPositionUrl) { var handleEditPosition = function() { IETF.ballotDialog.hide(); window.location = IETF.editPositionUrl; @@ -87,7 +87,7 @@ function showRecBallot(acronym, editPositionUrl) { if (!IETF.ballotDialog) { el = document.createElement("div"); - el.innerHTML = '<div id="ballot_dialog" style="visibility:hidden;"><div class="hd">Positions for <span id="ballot_dialog_name">draft-ietf-foo-bar</span></span></div><div class="bd"> <div id="ballot_dialog_body" style="overflow-y:scroll; height:400px;"></div> </div></div>'; + el.innerHTML = '<div id="ballot_dialog" style="visibility:hidden;"><div class="hd">Positions for <span id="ballot_dialog_name">wgacronym</span></span></div><div class="bd"> <div id="ballot_dialog_body" style="overflow-y:scroll; height:400px;"></div> </div></div>'; document.getElementById("ietf-extras").appendChild(el); var buttons = [{text:"Close", handler:handleClose, isDefault:true}]; @@ -116,7 +116,7 @@ function showRecBallot(acronym, editPositionUrl) { argument: null }, null); } -function editRecBallot(editPositionUrl) { +function editWGBallot(editPositionUrl) { window.open(editPositionUrl); } diff --git a/static/js/rec-change-state.js b/static/js/wg-change-state.js similarity index 84% rename from static/js/rec-change-state.js rename to static/js/wg-change-state.js index 413f2677a..49673cc1e 100644 --- a/static/js/rec-change-state.js +++ b/static/js/wg-change-state.js @@ -5,6 +5,7 @@ jQuery(document).ready(function () { } function setMessageDraft(state) { + if (jQuery("#id_state").val() != "conclude") { if (message[state]) { if (state == "infrev") { initial_time.show(); @@ -17,6 +18,9 @@ jQuery(document).ready(function () { } else { jQuery("#id_message").val(""); } + } else { + jQuery("#id_message").val(""); + } } jQuery("#id_charter_state").click(function (e) { @@ -24,4 +28,5 @@ jQuery(document).ready(function () { }); jQuery("#id_charter_state").click(); + }); diff --git a/static/js/rec-edit-position.js b/static/js/wg-edit-position.js similarity index 100% rename from static/js/rec-edit-position.js rename to static/js/wg-edit-position.js