Skip to content

Commit 204a4dd

Browse files
committed
Allow to edit the workflow used in a wg. Fixes ietf-tools#559
- Legacy-Id: 2733
1 parent c5dd835 commit 204a4dd

3 files changed

Lines changed: 332 additions & 28 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
{% extends "wginfo/wg_base.html" %}
2+
3+
{% block wg_titledetail %}Manage Workflow{% endblock %}
4+
5+
{% block wg_content %}
6+
<div class="wg-workflow-management">
7+
<h2>Edit workflow</h2>
8+
<div id="mytabs" class="yui-navset">
9+
<ul class="yui-nav">
10+
<li class="selected"><a href="#info"><em>Info</em></a></li>
11+
<li><a href="#states"><em>Select states</em></a></li>
12+
<li><a href="#transitions"><em>Edit transitions</em></a></li>
13+
<li><a href="#tags"><em>Select Annotation Tags</em></a></li>
14+
</ul>
15+
16+
<div class="yui-content">
17+
<div id="info">
18+
<table style="width: 100%;"><tr style="vertical-align: top;"><td>
19+
<table class="ietf-table" style="width: 100%">
20+
<tr>
21+
<th>States used in {{ wg }} Working Group</th>
22+
</tr>
23+
{% for state in states %}
24+
<tr class="{% cycle "oddrow" "evenrow" %}">
25+
<td>{{ state.name }}</td>
26+
</tr>
27+
{% endfor %}
28+
</table><br />
29+
<table class="ietf-table" style="width: 100%;">
30+
<tr>
31+
<th>Annotation tags used in {{ wg }} Working Group</th>
32+
</tr>
33+
{% for tag in tags %}
34+
<tr class="{% cycle "oddrow" "evenrow" %}">
35+
<td>{{ tag.name }}</td>
36+
</tr>
37+
{% endfor %}
38+
</table>
39+
</td><td>
40+
<table class="ietf-table" style="width: 100%;">
41+
<tr>
42+
<th>Transition name</th><th>Initial states</th><th>Destination state</th>
43+
</tr>
44+
{% for transition in workflow.transitions.all %}
45+
<tr class="{% cycle "oddrow" "evenrow" %}">
46+
<td>
47+
{{ transition.name }}
48+
</td>
49+
<td>
50+
{% for state in transition.states.all %}
51+
{{state.name }}{% if not forloop.last %}<br />{% endif %}
52+
{% endfor %}
53+
</td>
54+
<td>
55+
{{ transition.destination.name }}
56+
</td>
57+
</tr>
58+
{% endfor %}
59+
{% if not workflow.transitions.all.count %}
60+
<tr class="oddrow"><td colspan="3">There are no transitions defined so any state change is allowed</td></tr>
61+
{% endif %}
62+
</table>
63+
</td></tr></table>
64+
</div>
65+
66+
<div id="states">
67+
<form action="#info" method="POST">
68+
<table class="ietf-table">
69+
<tr>
70+
<th>Used in {{ wg }}</th><th>Available states</th>
71+
</tr>
72+
{% for state in default_states %}
73+
<tr class="{% cycle "oddrow" "evenrow" %}">
74+
<td><input type="checkbox" id="id_states_{{ state.pk }}" name="states" value="{{ state.pk }}" {% if state.used %}checked="checked" {% endif %}/></td>
75+
<td><label for="id_states_{{ state.pk }}">{{ state.name }}</label></td>
76+
</tr>
77+
{% endfor %}
78+
</table>
79+
<input type="submit" name="update_states" value="Update states" />
80+
</form>
81+
</div>
82+
83+
<div id="transitions">
84+
<form action="#transitions" method="POST">
85+
<table class="ietf-table">
86+
<tr>
87+
<th>Delete</th><th>Transition name</th><th>Initial states</th><th>Destination state</th>
88+
</tr>
89+
{{ formset.as_table }}
90+
</table>
91+
<input type="submit" name="update_transitions" value="Update transitions" />
92+
</form>
93+
</div>
94+
95+
<div id="tags">
96+
<form action="#info" method="POST">
97+
<table class="ietf-table">
98+
<tr>
99+
<th>Used in {{ wg }}</th><th>Available annotation tags</th>
100+
</tr>
101+
{% for tag in default_tags %}
102+
<tr class="{% cycle "oddrow" "evenrow" %}">
103+
<td><input type="checkbox" id="id_tags_{{ tag.pk }}" name="tags" value="{{ tag.pk }}" {% if tag.used %}checked="checked" {% endif %}/></td>
104+
<td><label for="id_tags_{{ tag.pk }}">{{ tag.name }}</label></td>
105+
</tr>
106+
{% endfor %}
107+
</table>
108+
<input type="submit" name="update_tags" value="Update tags" />
109+
</form>
110+
</div>
111+
</div>
112+
</div>
113+
114+
<script type="text/javascript" src="/js/lib/jquery-1.4.2.min.js"></script>
115+
<script type="text/javascript" src="/js/yui/yui-20100305.js"></script>
116+
<script type="text/javascript" src="/js/base.js"></script>
117+
118+
<script type="text/javascript">
119+
//<![CDATA[
120+
var tabView = new YAHOO.widget.TabView('mytabs');
121+
var url = location.href.split('#');
122+
if (url[1]) {
123+
url[1] = "#"+url[1];
124+
var tabs = tabView.get('tabs');
125+
for (var i = 0; i < tabs.length; i++) {
126+
if (url[1].indexOf(tabs[i].get('href')) == 0) {
127+
tabView.set('activeIndex', i);
128+
break;
129+
}
130+
}
131+
}
132+
//]]>
133+
</script>
134+
135+
{% endblock %}

ietf/wgchairs/forms.py

Lines changed: 149 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
from django.db.models import Q
33
from django.conf import settings
44
from django.core.mail import EmailMessage
5+
from django.forms.models import BaseModelFormSet
56
from django.template.loader import render_to_string
7+
from django.utils.safestring import mark_safe
68

79
from ietf.wgchairs.models import WGDelegate
810
from ietf.wgchairs.accounts import get_person_for_user
11+
from ietf.ietfworkflows.utils import get_default_workflow_for_wg, get_workflow_for_wg
912
from ietf.idtracker.models import PersonOrOrgInfo
1013

14+
from workflows.models import Transition
15+
1116

1217
class RelatedWGForm(forms.Form):
1318

@@ -28,6 +33,131 @@ def set_message(self, msg_type, msg_value):
2833
}
2934

3035

36+
class TagForm(RelatedWGForm):
37+
38+
tags = forms.ModelMultipleChoiceField(get_default_workflow_for_wg().annotation_tags.all(),
39+
widget=forms.CheckboxSelectMultiple)
40+
41+
def save(self):
42+
workflow = get_workflow_for_wg(self.wg)
43+
workflow.selected_tags.clear()
44+
for tag in self.cleaned_data['tags']:
45+
workflow.selected_tags.add(tag)
46+
return workflow
47+
48+
49+
class StateForm(RelatedWGForm):
50+
51+
states = forms.ModelMultipleChoiceField(get_default_workflow_for_wg().states.all(),
52+
widget=forms.CheckboxSelectMultiple)
53+
54+
def update_transitions(self, workflow):
55+
for transition in workflow.transitions.all():
56+
if not workflow.selected_states.filter(pk=transition.destination.pk).count():
57+
transition.delete()
58+
continue
59+
for state in transition.states.all():
60+
if not workflow.selected_states.filter(pk=state.pk).count():
61+
transition.states.remove(state)
62+
if not transition.states.count():
63+
transition.delete()
64+
continue
65+
66+
def save(self):
67+
workflow = get_workflow_for_wg(self.wg)
68+
workflow.selected_states.clear()
69+
for state in self.cleaned_data['states']:
70+
workflow.selected_states.add(state)
71+
self.update_transitions(workflow)
72+
return workflow
73+
74+
75+
class DeleteTransitionForm(RelatedWGForm):
76+
77+
transitions = forms.ModelMultipleChoiceField(Transition.objects.all(),
78+
widget=forms.CheckboxSelectMultiple)
79+
80+
def __init__(self, *args, **kwargs):
81+
super(DeleteTransitionForm, self).__init__(*args, **kwargs)
82+
workflow = get_workflow_for_wg(self.wg)
83+
self.fields['transitions'].queryset = self.fields['transitions'].queryset.filter(workflow=workflow)
84+
85+
def save(self):
86+
for transition in self.cleaned_data['transitions']:
87+
transition.delete()
88+
89+
90+
class TransitionForm(forms.ModelForm):
91+
92+
states = forms.ModelMultipleChoiceField(get_default_workflow_for_wg().states.all())
93+
94+
class Meta:
95+
model = Transition
96+
fields = ('DELETE', 'name', 'states', 'destination', )
97+
98+
def __init__(self, *args, **kwargs):
99+
self.wg = kwargs.pop('wg', None)
100+
self.user = kwargs.pop('user', None)
101+
super(TransitionForm, self).__init__(*args, **kwargs)
102+
workflow = get_workflow_for_wg(self.wg)
103+
self.fields['states'].queryset = workflow.selected_states.all()
104+
self.fields['destination'].queryset = workflow.selected_states.all()
105+
self.fields['destination'].required = True
106+
if self.instance.pk:
107+
self.fields['states'].initial = [i.pk for i in self.instance.states.all()]
108+
self.instance.workflow = workflow
109+
110+
def as_row(self):
111+
return self._html_output(u'<td>%(errors)s%(field)s%(help_text)s</td>', u'<td colspan="2">%s</td>', '</td>', u'<br />%s', False)
112+
113+
def save(self, *args, **kwargs):
114+
instance = super(TransitionForm, self).save(*args, **kwargs)
115+
for state in self.cleaned_data['states']:
116+
state.transitions.add(instance)
117+
118+
119+
class TransitionFormSet(BaseModelFormSet):
120+
121+
form = TransitionForm
122+
can_delete = True
123+
extra = 2
124+
max_num = 0
125+
can_order = False
126+
model = Transition
127+
128+
def __init__(self, *args, **kwargs):
129+
self.wg = kwargs.pop('wg', None)
130+
self.user = kwargs.pop('user', None)
131+
super(TransitionFormSet, self).__init__(*args, **kwargs)
132+
133+
def _construct_form(self, i, **kwargs):
134+
kwargs = kwargs or {}
135+
kwargs.update({'wg': self.wg, 'user': self.user})
136+
return super(TransitionFormSet, self)._construct_form(i, **kwargs)
137+
138+
def as_table(self):
139+
html = u''
140+
csscl = 'oddrow'
141+
for form in self.forms:
142+
html += u'<tr class="%s">' % csscl
143+
html += form.as_row()
144+
html += u'</tr>'
145+
if csscl == 'oddrow':
146+
csscl = 'evenrow'
147+
else:
148+
csscl = 'oddrow'
149+
return mark_safe(u'\n'.join([unicode(self.management_form), html]))
150+
151+
152+
def workflow_form_factory(request, wg, user):
153+
154+
if request.POST.get('update_transitions', None):
155+
return TransitionFormSet(wg=wg, user=user, data=request.POST)
156+
elif request.POST.get('update_states', None):
157+
return StateForm(wg=wg, user=user, data=request.POST)
158+
return TagForm(wg=wg, user=user, data=request.POST)
159+
160+
31161
class RemoveDelegateForm(RelatedWGForm):
32162

33163
delete = forms.MultipleChoiceField()
@@ -148,7 +278,6 @@ def send_email(self, email, template):
148278
from_email=settings.DEFAULT_FROM_EMAIL)
149279
mail.send()
150280

151-
152281
def send_email_to_delegate(self, email):
153282
self.send_email(email, 'wgchairs/notexistsdelegate_delegate_email.txt')
154283

@@ -187,55 +316,52 @@ def add_form_factory(request, wg, user):
187316
class ManagingShepherdForm(forms.Form):
188317
email = forms.EmailField(required=False)
189318
is_assign_current = forms.BooleanField(required=False)
190-
191-
319+
192320
def __init__(self, *args, **kwargs):
193-
if kwargs.has_key('current_person'):
194-
self.current_person = kwargs.pop('current_person')
321+
if 'current_person' in kwargs.keys():
322+
self.current_person = kwargs.pop('current_person')
195323
return super(ManagingShepherdForm, self).__init__(*args, **kwargs)
196-
324+
197325
def clean_email(self):
198326
email = self.cleaned_data.get('email')
199327
if not email:
200328
return None
201-
329+
202330
try:
203331
PersonOrOrgInfo.objects. \
204-
filter(emailaddress__type__in=[ "INET", "Prim",],
205-
emailaddress__address=email) \
206-
[:1].get()
332+
filter(emailaddress__type__in=["INET", "Prim", ],
333+
emailaddress__address=email)[:1].get()
207334
except PersonOrOrgInfo.DoesNotExist:
208335
if self.cleaned_data.get('is_assign_current'):
209336
self._send_email(email)
210337
raise forms.ValidationError("Person with such email does not exist")
211338
return email
212-
339+
213340
def clean(self):
214341
print self.cleaned_data.get('email') and self.cleaned_data.get('is_assign_current')
215342
if self.cleaned_data.get('email') and \
216343
self.cleaned_data.get('is_assign_current'):
217344
raise forms.ValidationError("You should choose to assign to current \
218345
person or input the email. Not both at te same time. ")
219-
346+
220347
return self.cleaned_data
221-
348+
222349
def change_shepherd(self, document, save=True):
223-
email = self.cleaned_data.get('email')
350+
email = self.cleaned_data.get('email')
224351
if email:
225352
person = PersonOrOrgInfo.objects. \
226-
filter(emailaddress__type__in=[ "INET", "Prim",],
227-
emailaddress__address=email) \
228-
[:1].get()
353+
filter(emailaddress__type__in=["INET", "Prim", ],
354+
emailaddress__address=email)[:1].get()
229355
else:
230-
person = self.current_person
231-
document.shepherd = person
232-
if save:
356+
person = self.current_person
357+
document.shepherd = person
358+
if save:
233359
document.save()
234360
return document
235-
236-
def _send_email(self, email,
361+
362+
def _send_email(self, email,
237363
template='wgchairs/edit_management_shepherd_email.txt'):
238-
subject = 'WG Delegate needs system credentials'
364+
subject = 'WG Delegate needs system credentials'
239365
body = render_to_string(template,
240366
{'email': email,
241367
})

0 commit comments

Comments
 (0)