Skip to content

Commit 256dd73

Browse files
committed
Detailed stream info view. Fixes ietf-tools#573
- Legacy-Id: 2758
1 parent d516f43 commit 256dd73

15 files changed

Lines changed: 402 additions & 13 deletions

File tree

ietf/ietfworkflows/models.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ class ObjectHistoryEntry(models.Model):
1717
comment = models.TextField(_('Comment'))
1818
person = models.ForeignKey(PersonOrOrgInfo)
1919

20+
class Meta:
21+
ordering = ('-date', )
22+
23+
2024
def get_real_instance(self):
2125
if hasattr(self, '_real_instance'):
2226
return self._real_instance
2327
for i in ('objectworkflowhistoryentry', 'objectannotationtaghistoryentry', 'objectstreamhistoryentry'):
2428
try:
25-
real_instance = getattr(self, 'objectworkflowhistoryentry', None)
29+
real_instance = getattr(self, i, None)
2630
if real_instance:
2731
self._real_instance = real_instance
2832
return real_instance
@@ -36,16 +40,42 @@ class ObjectWorkflowHistoryEntry(ObjectHistoryEntry):
3640
from_state = models.CharField(_('From state'), max_length=100)
3741
to_state = models.CharField(_('To state'), max_length=100)
3842

43+
def describe_change(self):
44+
html = '<p class="describe_state_change">'
45+
html += 'Changed state <i>%s</i> to <b>%s</b>' % (self.from_state, self.to_state)
46+
html += '</p>'
47+
return html
48+
3949

4050
class ObjectAnnotationTagHistoryEntry(ObjectHistoryEntry):
4151
setted = models.TextField(_('Setted tags'), blank=True, null=True)
4252
unsetted = models.TextField(_('Unsetted tags'), blank=True, null=True)
4353

54+
def describe_change(self):
55+
html = ''
56+
if self.setted:
57+
html += '<p class="describe_tags_set">'
58+
html += 'Annotation tags set: '
59+
html += self.setted
60+
html += '</p>'
61+
if self.unsetted:
62+
html += '<p class="describe_tags_reset">'
63+
html += 'Annotation tags reset: '
64+
html += self.unsetted
65+
html += '</p>'
66+
return html
67+
4468

4569
class ObjectStreamHistoryEntry(ObjectHistoryEntry):
4670
from_stream = models.TextField(_('From stream'), blank=True, null=True)
4771
to_stream = models.TextField(_('To stream'), blank=True, null=True)
4872

73+
def describe_change(self):
74+
html = '<p class="describe_stream_change">'
75+
html += 'Changed doc from stream <i>%s</i> to <b>%s</b>' % (self.from_stream, self.to_stream)
76+
html += '</p>'
77+
return html
78+
4979

5080
class AnnotationTag(models.Model):
5181
name = models.CharField(_(u"Name"), max_length=100)

ietf/ietfworkflows/streams.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from django.db import models
2+
3+
from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper
4+
from ietf.ietfworkflows.models import StreamedID, Stream
5+
6+
7+
def get_streamed_draft(draft):
8+
if not draft:
9+
return None
10+
try:
11+
return draft.streamedid
12+
except StreamedID.DoesNotExist:
13+
return None
14+
15+
16+
def get_stream_from_draft(draft):
17+
streamedid = get_streamed_draft(draft)
18+
if streamedid:
19+
return streamedid.stream
20+
return False
21+
22+
23+
def get_stream_from_id(stream_id):
24+
try:
25+
return Stream.objects.get(id=stream_id)
26+
except Stream.DoesNotExist:
27+
return None
28+
29+
30+
def _get_group_from_acronym(group_model_str, acronym):
31+
try:
32+
app, model = group_model_str.split('.', 1)
33+
except ValueError:
34+
return None
35+
group_model = models.get_model(app, model)
36+
if not group_model:
37+
return
38+
if 'acronym' in group_model._meta.get_all_field_names():
39+
try:
40+
return group_model._default_manager.get(acronym=acronym)
41+
except group_model.DoesNotExist:
42+
return None
43+
elif 'group_acronym' in group_model._meta.get_all_field_names():
44+
try:
45+
return group_model._default_manager.get(group_acronym__acronym=acronym)
46+
except group_model.DoesNotExist:
47+
return None
48+
else:
49+
return None
50+
51+
52+
def _set_stream_automatically(draft, stream):
53+
streamed = StreamedID.objects.create(stream=stream, draft=draft)
54+
if not stream or not stream.with_groups:
55+
return
56+
try:
57+
draft_literal, stream_name, group_name, extra = draft.filename.split('-', 3)
58+
if stream_name.lower() == stream.name.lower():
59+
group = _get_group_from_acronym(stream.group_model, group_name)
60+
if group:
61+
streamed.group = group
62+
streamed.save()
63+
except ValueError:
64+
return
65+
66+
67+
def get_stream_from_wrapper(idrfc_wrapper):
68+
idwrapper = None
69+
if isinstance(idrfc_wrapper, IdRfcWrapper):
70+
idwrapper = idrfc_wrapper.id
71+
elif isinstance(idrfc_wrapper, IdWrapper):
72+
idwrapper = idrfc_wrapper
73+
if not idwrapper:
74+
return None
75+
draft = idwrapper._draft
76+
stream = get_stream_from_draft(draft)
77+
if stream == False:
78+
stream_id = idwrapper.stream_id()
79+
stream = get_stream_from_id(stream_id)
80+
_set_stream_automatically(draft, stream)
81+
return stream
82+
else:
83+
return stream
84+
return None

ietf/ietfworkflows/templatetags/__init__.py

Whitespace-only changes.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from django import template
2+
3+
from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper
4+
from ietf.ietfworkflows.utils import (get_workflow_for_draft,
5+
get_state_for_draft)
6+
from ietf.ietfworkflows.streams import get_stream_from_wrapper
7+
8+
9+
register = template.Library()
10+
11+
12+
@register.inclusion_tag('ietfworkflows/stream_state.html', takes_context=True)
13+
def stream_state(context, doc):
14+
request = context.get('request', None)
15+
user = request and request.user
16+
data = {}
17+
stream = get_stream_from_wrapper(doc)
18+
data.update({'stream': stream})
19+
if not stream:
20+
return data
21+
22+
idwrapper = None
23+
if isinstance(doc, IdRfcWrapper):
24+
idwrapper = doc.id
25+
elif isinstance(doc, IdWrapper):
26+
idwrapper = doc
27+
if not idwrapper:
28+
return data
29+
30+
draft = getattr(idwrapper, '_draft', None)
31+
if not draft:
32+
return data
33+
34+
workflow = get_workflow_for_draft(draft)
35+
state = get_state_for_draft(draft)
36+
37+
data.update({'workflow': workflow,
38+
'draft': draft,
39+
'state': state})
40+
41+
return data
42+
43+
44+
@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True)
45+
def workflow_history_entry(context, entry):
46+
real_entry = entry.get_real_instance()
47+
return {'entry': real_entry,
48+
'entry_class': real_entry.__class__.__name__.lower()}

ietf/ietfworkflows/urls.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright The IETF Trust 2008, All Rights Reserved
2+
3+
from django.conf.urls.defaults import patterns, url
4+
5+
urlpatterns = patterns('ietf.ietfworkflows.views',
6+
url(r'^(?P<name>[^/]+)/history/$', 'stream_history', name='stream_history'),
7+
)

ietf/ietfworkflows/utils.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
from workflows.utils import (get_workflow_for_object, set_workflow_for_object,
1111
get_state)
1212

13+
from ietf.ietfworkflows.streams import get_streamed_draft
1314
from ietf.ietfworkflows.models import (WGWorkflow, AnnotationTagObjectRelation,
14-
AnnotationTag, ObjectAnnotationTagHistoryEntry)
15+
AnnotationTag, ObjectAnnotationTagHistoryEntry,
16+
ObjectHistoryEntry)
1517

1618

1719
WAITING_WRITEUP = 'WG Consensus: Waiting for Write-Up'
@@ -24,7 +26,8 @@ def get_default_workflow_for_wg():
2426
return workflow
2527
except WGWorkflow.DoesNotExist:
2628
return None
27-
29+
30+
2831
def clone_transition(transition):
2932
new = copy.copy(transition)
3033
new.pk = None
@@ -35,6 +38,7 @@ def clone_transition(transition):
3538
new.states.add(state)
3639
return new
3740

41+
3842
def clone_workflow(workflow, name):
3943
new = WGWorkflow.objects.create(name=name, initial_state=workflow.initial_state)
4044

@@ -51,32 +55,55 @@ def clone_workflow(workflow, name):
5155
new.transitions.add(clone_transition(transition))
5256
return new
5357

54-
def get_workflow_for_wg(wg):
58+
59+
def get_workflow_for_wg(wg, default=None):
5560
workflow = get_workflow_for_object(wg)
5661
try:
5762
workflow = workflow and workflow.wgworkflow
5863
except WGWorkflow.DoesNotExist:
5964
workflow = None
6065
if not workflow:
61-
workflow = get_default_workflow_for_wg()
66+
if default:
67+
workflow = default
68+
else:
69+
workflow = get_default_workflow_for_wg()
6270
if not workflow:
6371
return None
6472
workflow = clone_workflow(workflow, name='%s workflow' % wg)
6573
set_workflow_for_object(wg, workflow)
6674
return workflow
6775

76+
6877
def get_workflow_for_draft(draft):
6978
workflow = get_workflow_for_object(draft)
7079
try:
7180
workflow = workflow and workflow.wgworkflow
7281
except WGWorkflow.DoesNotExist:
7382
workflow = None
7483
if not workflow:
75-
workflow = get_workflow_for_wg(draft.group.ietfwg)
84+
streamed_draft = get_streamed_draft(draft)
85+
if not streamed_draft or not streamed_draft.stream:
86+
return None
87+
stream = streamed_draft.stream
88+
if stream.with_groups:
89+
if not streamed_draft.group:
90+
return None
91+
else:
92+
workflow = get_workflow_for_wg(streamed_draft.group, streamed_draft.stream.workflow)
93+
else:
94+
workflow = stream.workflow
7695
set_workflow_for_object(draft, workflow)
7796
return workflow
7897

7998

99+
def get_workflow_history_for_draft(draft):
100+
ctype = ContentType.objects.get_for_model(draft)
101+
history = ObjectHistoryEntry.objects.filter(content_type=ctype, content_id=draft.pk).\
102+
select_related('objectworkflowhistoryentry', 'objectannotationtaghistoryentry',
103+
'objectstreamhistoryentry')
104+
return history
105+
106+
80107
def get_annotation_tags_for_draft(draft):
81108
ctype = ContentType.objects.get_for_model(draft)
82109
tags = AnnotationTagObjectRelation.objects.filter(content_type=ctype, content_id=draft.pk)
@@ -100,6 +127,7 @@ def get_annotation_tag_by_name(tag_name):
100127
except AnnotationTag.DoesNotExist:
101128
return None
102129

130+
103131
def set_tag_by_name(obj, tag_name):
104132
ctype = ContentType.objects.get_for_model(obj)
105133
try:
@@ -159,7 +187,7 @@ def notify_tag_entry(entry, extra_notify=[]):
159187
def notify_state_entry(entry, extra_notify=[]):
160188
return notify_entry(entry, 'ietfworkflows/state_updated_mail.txt', extra_notify)
161189

162-
190+
163191
def update_tags(obj, comment, person, set_tags=[], reset_tags=[], extra_notify=[]):
164192
ctype = ContentType.objects.get_for_model(obj)
165193
setted = []
@@ -173,9 +201,9 @@ def update_tags(obj, comment, person, set_tags=[], reset_tags=[], extra_notify=[
173201
entry = ObjectAnnotationTagHistoryEntry.objects.create(
174202
content_type=ctype,
175203
content_id=obj.pk,
176-
setted = ','.join(setted),
177-
unsetted = ','.join(resetted),
178-
change_date = datetime.datetime.now(),
179-
comment = comment,
204+
setted=','.join(setted),
205+
unsetted=','.join(resetted),
206+
date=datetime.datetime.now(),
207+
comment=comment,
180208
person=person)
181209
notify_tag_entry(entry, extra_notify)

ietf/ietfworkflows/views.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from ietf.idtracker.models import IETFWG, InternetDraft, IESGLogin
2+
from django.shortcuts import get_object_or_404, render_to_response
3+
from django.template import RequestContext
4+
from django.http import HttpResponseForbidden, Http404
5+
6+
from ietf.idrfc.views_search import SearchForm, search_query
7+
from ietf.wgchairs.forms import (RemoveDelegateForm, add_form_factory,
8+
workflow_form_factory, TransitionFormSet,
9+
WriteUpEditForm)
10+
from ietf.wgchairs.accounts import (can_manage_delegates_in_group, get_person_for_user,
11+
can_manage_shepherds_in_group,
12+
can_manage_workflow_in_group,
13+
can_manage_shepherd_of_a_document,
14+
can_manage_writeup_of_a_document,
15+
can_manage_writeup_of_a_document_no_state,
16+
)
17+
from ietf.ietfworkflows.streams import (get_stream_from_draft,
18+
get_streamed_draft)
19+
from ietf.ietfworkflows.utils import (get_workflow_for_wg,
20+
get_default_workflow_for_wg,
21+
get_workflow_history_for_draft,
22+
get_workflow_for_draft,
23+
get_state_by_name,
24+
get_annotation_tags_for_draft,
25+
get_state_for_draft, WAITING_WRITEUP,
26+
FOLLOWUP_TAG)
27+
28+
29+
REDUCED_HISTORY_LEN = 20
30+
31+
32+
def stream_history(request, name):
33+
user = request.user
34+
person = get_person_for_user(user)
35+
draft = get_object_or_404(InternetDraft, filename=name)
36+
streamed = get_streamed_draft(draft)
37+
stream = get_stream_from_draft(draft)
38+
workflow = get_workflow_for_draft(draft)
39+
tags = []
40+
if workflow:
41+
tags_setted = [i.annotation_tag.pk for i in get_annotation_tags_for_draft(draft)]
42+
for tag in workflow.get_tags():
43+
tag.setted = tag.pk in tags_setted
44+
tags.append(tag)
45+
state = get_state_for_draft(draft)
46+
history = get_workflow_history_for_draft(draft)
47+
show_more = False
48+
if history.count > REDUCED_HISTORY_LEN:
49+
show_more = True
50+
return render_to_response('ietfworkflows/stream_history.html',
51+
{'stream': stream,
52+
'streamed': streamed,
53+
'draft': draft,
54+
'tags': tags,
55+
'state': state,
56+
'workflow': workflow,
57+
'show_more': show_more,
58+
'history': history[:REDUCED_HISTORY_LEN],
59+
},
60+
context_instance=RequestContext(request))

0 commit comments

Comments
 (0)