Skip to content

Commit a7667fb

Browse files
committed
Merged in branch/amsl/liaisons/6.4.1@10160 from rcross@amsl.com, bringing in the new liaison tool.
- Legacy-Id: 10161
7 parents c438eb0 + 64cdd2e + 594d626 + 3397b2d + 70830b6 + 1df82ab + a071764 commit a7667fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4559
-2662
lines changed

ietf/ietfauth/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def has_role(user, role_names, *args, **kwargs):
5454
"IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
5555
"IRTF Chair": Q(person=person, name="chair", group__acronym="irtf"),
5656
"IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
57+
"IAB Executive Director": Q(person=person, name="execdir", group__acronym="iab"),
5758
"IAB Group Chair": Q(person=person, name="chair", group__type="iab", group__state="active"),
5859
"WG Chair": Q(person=person,name="chair", group__type="wg", group__state__in=["active","bof", "proposed"]),
5960
"WG Secretary": Q(person=person,name="secr", group__type="wg", group__state__in=["active","bof", "proposed"]),
@@ -64,6 +65,8 @@ def has_role(user, role_names, *args, **kwargs):
6465
"Nomcom Chair": Q(person=person, name="chair", group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
6566
"Nomcom Advisor": Q(person=person, name="advisor", group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
6667
"Nomcom": Q(person=person, group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
68+
"Liaison Manager": Q(person=person,name="liaiman",group__type="sdo",group__state="active", ),
69+
"Authorized Individual": Q(person=person,name="auth",group__type="sdo",group__state="active", ),
6770
}
6871

6972
filter_expr = Q()

ietf/liaisons/accounts.py

Lines changed: 0 additions & 146 deletions
This file was deleted.

ietf/liaisons/admin.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,44 @@
11
from django.contrib import admin
2+
from django.core.urlresolvers import reverse
23

3-
from ietf.liaisons.models import LiaisonStatement
4+
from ietf.liaisons.models import ( LiaisonStatement, LiaisonStatementEvent,
5+
LiaisonStatementGroupContacts, RelatedLiaisonStatement, LiaisonStatementAttachment )
6+
7+
8+
class RelatedLiaisonStatementInline(admin.TabularInline):
9+
model = RelatedLiaisonStatement
10+
fk_name = 'source'
11+
raw_id_fields = ['target']
12+
extra = 1
13+
14+
class LiaisonStatementAttachmentInline(admin.TabularInline):
15+
model = LiaisonStatementAttachment
16+
raw_id_fields = ['document']
17+
extra = 1
418

519
class LiaisonStatementAdmin(admin.ModelAdmin):
6-
list_display = ['id', 'title', 'from_name', 'to_name', 'submitted', 'purpose', 'related_to']
20+
list_display = ['id', 'title', 'submitted', 'from_groups_short_display', 'purpose', 'related_to']
721
list_display_links = ['id', 'title']
822
ordering = ('title', )
9-
raw_id_fields = ('from_contact', 'related_to', 'from_group', 'to_group', 'attachments')
23+
raw_id_fields = ('from_contact', 'attachments', 'from_groups', 'to_groups')
24+
#filter_horizontal = ('from_groups', 'to_groups')
25+
inlines = [ RelatedLiaisonStatementInline, LiaisonStatementAttachmentInline ]
26+
27+
def related_to(self, obj):
28+
return '<br />'.join(['<a href="%s">%s</a>' % (reverse('admin:liaisons_liaisonstatement_change', None, (i.target.id, )), str(i.target)) for i in obj.source_of_set.select_related('target').all()])
29+
related_to.allow_tags = True
30+
31+
class LiaisonStatementEventAdmin(admin.ModelAdmin):
32+
list_display = ["statement", "type", "by", "time"]
33+
search_fields = ["statement__title", "by__name"]
34+
raw_id_fields = ["statement", "by"]
35+
36+
class LiaisonStatementGroupContactsAdmin(admin.ModelAdmin):
37+
list_display = ["group", "contacts"]
38+
raw_id_fields = ["group"]
39+
search_fields = ["group__acronym", "contacts"]
40+
ordering = ["group__name"]
41+
1042
admin.site.register(LiaisonStatement, LiaisonStatementAdmin)
43+
admin.site.register(LiaisonStatementEvent, LiaisonStatementEventAdmin)
44+
admin.site.register(LiaisonStatementGroupContacts, LiaisonStatementGroupContactsAdmin)

ietf/liaisons/feeds.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@
1616
# to construct a queryset.
1717
class LiaisonStatementsFeed(Feed):
1818
feed_type = Atom1Feed
19-
link = reverse_lazy("liaison_list")
19+
link = reverse_lazy("ietf.liaisons.views.liaison_list")
2020
description_template = "liaisons/feed_item_description.html"
2121

2222
def get_object(self, request, kind, search=None):
2323
obj = {}
2424

2525
if kind == 'recent':
2626
obj['title'] = 'Recent Liaison Statements'
27-
obj['limit'] = 15
28-
return obj
27+
obj['limit'] = 15
28+
return obj
2929

3030
if kind == 'from':
3131
if not search:
3232
raise FeedDoesNotExist
3333

3434
try:
3535
group = Group.objects.get(acronym=search)
36-
obj['filter'] = { 'from_group': group }
36+
obj['filter'] = { 'from_groups': group }
3737
obj['title'] = u'Liaison Statements from %s' % group.name
3838
return obj
3939
except Group.DoesNotExist:
@@ -54,46 +54,47 @@ def get_object(self, request, kind, search=None):
5454
if not search:
5555
raise FeedDoesNotExist
5656

57-
obj['filter'] = dict(to_name__icontains=search)
58-
obj['title'] = 'Liaison Statements where to matches %s' % search
57+
group = Group.objects.get(acronym=search)
58+
obj['filter'] = { 'to_groups': group }
59+
obj['title'] = u'Liaison Statements to %s' % group.name
5960
return obj
6061

61-
if kind == 'subject':
62+
if kind == 'subject':
6263
if not search:
63-
raise FeedDoesNotExist
64+
raise FeedDoesNotExist
6465

6566
obj['q'] = [ Q(title__icontains=search) | Q(attachments__title__icontains=search) ]
6667
obj['title'] = 'Liaison Statements where subject matches %s' % search
6768
return obj
6869

69-
raise FeedDoesNotExist
70+
raise FeedDoesNotExist
7071

7172
def items(self, obj):
72-
qs = LiaisonStatement.objects.all().order_by("-submitted")
73-
if obj.has_key('q'):
74-
qs = qs.filter(*obj['q'])
75-
if obj.has_key('filter'):
76-
qs = qs.filter(**obj['filter'])
77-
if obj.has_key('limit'):
78-
qs = qs[:obj['limit']]
79-
return qs
73+
qs = LiaisonStatement.objects.all().order_by("-id")
74+
if obj.has_key('q'):
75+
qs = qs.filter(*obj['q'])
76+
if obj.has_key('filter'):
77+
qs = qs.filter(**obj['filter'])
78+
if obj.has_key('limit'):
79+
qs = qs[:obj['limit']]
80+
return qs
8081

8182
def title(self, obj):
82-
return obj['title']
83+
return obj['title']
8384

8485
def description(self, obj):
85-
return self.title(obj)
86+
return self.title(obj)
8687

8788
def item_title(self, item):
8889
return render_to_string("liaisons/liaison_title.html", { 'liaison': item }).strip()
8990

9091
def item_link(self, item):
91-
return urlreverse("liaison_detail", kwargs={ "object_id": item.pk })
92+
return urlreverse("ietf.liaisons.views.liaison_detail", kwargs={ "object_id": item.pk })
9293

9394
def item_pubdate(self, item):
9495
# this method needs to return a datetime instance, even
95-
# though the database has only date, not time
96+
# though the database has only date, not time
9697
return item.submitted
97-
98+
9899
def item_author_name(self, item):
99100
return item.from_name

ietf/liaisons/fields.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,65 @@
77
from ietf.liaisons.models import LiaisonStatement
88

99
def select2_id_liaison_json(objs):
10-
return json.dumps([{ "id": o.pk, "text": escape(o.title) } for o in objs])
10+
return json.dumps([{ "id": o.pk, "text":u"[{}] {}".format(o.pk, escape(o.title)) } for o in objs])
1111

12-
class SearchableLiaisonStatementField(forms.IntegerField):
12+
def select2_id_group_json(objs):
13+
return json.dumps([{ "id": o.pk, "text": escape(o.acronym) } for o in objs])
14+
15+
class SearchableLiaisonStatementsField(forms.CharField):
1316
"""Server-based multi-select field for choosing liaison statements using
1417
select2.js."""
1518

16-
def __init__(self, hint_text="Type in title to search for document", *args, **kwargs):
17-
super(SearchableLiaisonStatementField, self).__init__(*args, **kwargs)
19+
def __init__(self,
20+
max_entries = None,
21+
hint_text="Type in title to search for document",
22+
model = LiaisonStatement,
23+
*args, **kwargs):
24+
kwargs["max_length"] = 10000
25+
self.model = model
26+
self.max_entries = max_entries
27+
28+
super(SearchableLiaisonStatementsField, self).__init__(*args, **kwargs)
1829

19-
self.widget.attrs["class"] = "select2-field"
30+
self.widget.attrs["class"] = "select2-field form-control"
2031
self.widget.attrs["data-placeholder"] = hint_text
21-
self.widget.attrs["data-max-entries"] = 1
32+
if self.max_entries != None:
33+
self.widget.attrs["data-max-entries"] = self.max_entries
34+
35+
def parse_select2_value(self, value):
36+
return [x.strip() for x in value.split(",") if x.strip()]
2237

2338
def prepare_value(self, value):
2439
if not value:
25-
value = None
26-
elif isinstance(value, LiaisonStatement):
27-
value = value
28-
else:
29-
value = LiaisonStatement.objects.exclude(approved=None).filter(pk=value).first()
40+
value = ""
41+
if isinstance(value, (int, long)):
42+
value = str(value)
43+
if isinstance(value, basestring):
44+
pks = self.parse_select2_value(value)
45+
value = self.model.objects.filter(pk__in=pks)
46+
if isinstance(value, LiaisonStatement):
47+
value = [value]
3048

31-
self.widget.attrs["data-pre"] = select2_id_liaison_json([value] if value else [])
49+
self.widget.attrs["data-pre"] = select2_id_liaison_json(value)
3250

3351
# doing this in the constructor is difficult because the URL
3452
# patterns may not have been fully constructed there yet
35-
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_liaison_statements")
53+
self.widget.attrs["data-ajax-url"] = urlreverse("ietf.liaisons.views.ajax_select2_search_liaison_statements")
3654

37-
return value
55+
return u",".join(unicode(o.pk) for o in value)
3856

3957
def clean(self, value):
40-
value = super(SearchableLiaisonStatementField, self).clean(value)
58+
value = super(SearchableLiaisonStatementsField, self).clean(value)
59+
pks = self.parse_select2_value(value)
4160

42-
if value == None:
43-
return None
61+
objs = self.model.objects.filter(pk__in=pks)
4462

45-
obj = LiaisonStatement.objects.filter(pk=value).first()
46-
if not obj and self.required:
47-
raise forms.ValidationError(u"You must select a value.")
63+
found_pks = [str(o.pk) for o in objs]
64+
failed_pks = [x for x in pks if x not in found_pks]
65+
if failed_pks:
66+
raise forms.ValidationError(u"Could not recognize the following groups: {pks}.".format(pks=", ".join(failed_pks)))
4867

49-
return obj
68+
if self.max_entries != None and len(objs) > self.max_entries:
69+
raise forms.ValidationError(u"You can select at most %s entries only." % self.max_entries)
5070

71+
return objs

0 commit comments

Comments
 (0)