Skip to content

Commit abb74d9

Browse files
committed
Search form for liaison statement list. See ietf-tools#1434
- Legacy-Id: 8049
1 parent f72b0a1 commit abb74d9

7 files changed

Lines changed: 138 additions & 22 deletions

File tree

ietf/liaisons/forms.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@
44
from django import forms
55
from django.conf import settings
66
from django.forms.util import ErrorList
7+
from django.db.models import Q
8+
from django.forms.widgets import RadioFieldRenderer
79
from django.core.validators import validate_email, ValidationError
810
from django.template.loader import render_to_string
11+
from django.utils.html import format_html
12+
from django.utils.encoding import force_text
13+
from django.utils.safestring import mark_safe
14+
915

1016
from ietf.liaisons.accounts import (can_add_outgoing_liaison, can_add_incoming_liaison,
1117
get_person_for_user, is_secretariat, is_sdo_liaison_manager)
1218
from ietf.liaisons.utils import IETFHM
1319
from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget,
1420
ShowAttachmentsWidget, RelatedLiaisonWidget)
15-
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
21+
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName, LiaisonStatementEvent
1622
from ietf.group.models import Group, Role
1723
from ietf.person.models import Person, Email
1824
from ietf.doc.models import Document
@@ -468,3 +474,63 @@ def liaison_form_factory(request, **kwargs):
468474
return IncomingLiaisonForm(user, **kwargs)
469475
return None
470476

477+
478+
class RadioRenderer(RadioFieldRenderer):
479+
480+
def render(self):
481+
output = []
482+
for widget in self:
483+
output.append(format_html(force_text(widget)))
484+
return mark_safe('\n'.join(output))
485+
486+
487+
class SearchLiaisonForm(forms.Form):
488+
489+
text = forms.CharField(required=False)
490+
scope = forms.ChoiceField(choices=(("all", "All text fields"), ("title", "Title field")), required=False, initial='title', widget=forms.RadioSelect(renderer=RadioRenderer))
491+
source = forms.CharField(required=False)
492+
destination = forms.CharField(required=False)
493+
start_date = forms.DateField(required=False, help_text="Format: YYYY-MM-DD")
494+
end_date = forms.DateField(required=False, help_text="Format: YYYY-MM-DD")
495+
496+
def get_results(self):
497+
results = LiaisonStatement.objects.filter(state__slug='approved').extra(
498+
select={
499+
'_submitted': 'SELECT time FROM liaisons_liaisonstatementevent WHERE liaisons_liaisonstatement.id = liaisons_liaisonstatementevent.statement_id AND liaisons_liaisonstatementevent.type_id = "submit"',
500+
'from_concat': 'SELECT GROUP_CONCAT(name SEPARATOR ", ") FROM group_group JOIN liaisons_liaisonstatement_from_groups WHERE liaisons_liaisonstatement.id = liaisons_liaisonstatement_from_groups.liaisonstatement_id AND liaisons_liaisonstatement_from_groups.group_id = group_group.id',
501+
'to_concat': 'SELECT GROUP_CONCAT(name SEPARATOR ", ") FROM group_group JOIN liaisons_liaisonstatement_to_groups WHERE liaisons_liaisonstatement.id = liaisons_liaisonstatement_to_groups.liaisonstatement_id AND liaisons_liaisonstatement_to_groups.group_id = group_group.id',
502+
})
503+
if self.is_bound:
504+
query = self.cleaned_data.get('text')
505+
if query:
506+
if self.cleaned_data.get('scope') == 'title':
507+
q = Q(title__icontains=query)
508+
else:
509+
q = (Q(title__icontains=query) | Q(other_identifiers__icontains=query) | Q(body__icontains=query) | Q(attachments__title__icontains=query) |
510+
Q(response_contacts__icontains=query) | Q(technical_contacts__icontains=query) | Q(action_holder_contacts__icontains=query) |
511+
Q(cc_contacts=query))
512+
results = results.filter(q)
513+
source = self.cleaned_data.get('source')
514+
if source:
515+
results = results.filter(Q(from_groups__name__icontains=source) | Q(from_groups__acronym__iexact=source) | Q(from_name__icontains=source))
516+
destination = self.cleaned_data.get('destination')
517+
if destination:
518+
results = results.filter(Q(to_groups__name__icontains=destination) | Q(to_groups__acronym__iexact=destination) | Q(to_name__icontains=destination))
519+
start_date = self.cleaned_data.get('start_date')
520+
end_date = self.cleaned_data.get('end_date')
521+
events = None
522+
if start_date:
523+
events = LiaisonStatementEvent.objects.filter(type='submit', time__gte=start_date)
524+
if end_date:
525+
events = events.filter(time__lte=end_date)
526+
elif end_date:
527+
events = LiaisonStatementEvent.objects.filter(type='submit', time__lte=end_date)
528+
if events:
529+
results = results.filter(liaisonstatementevent__in=events)
530+
531+
532+
destination = self.cleaned_data.get('destination')
533+
if destination:
534+
results = results.filter(Q(to_groups__name__icontains=destination) | Q(to_groups__acronym__iexact=destination) | Q(to_name__icontains=destination))
535+
results = results.distinct().order_by('title')
536+
return results

ietf/liaisons/models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class LiaisonStatement(models.Model):
3636

3737
attachments = models.ManyToManyField(Document, through='LiaisonStatementAttachments', blank=True)
3838

39+
state = models.ForeignKey(LiaisonStatementState, default='pending')
40+
3941
def name(self):
4042
if self.from_group:
4143
frm = self.from_group.acronym or self.from_group.name
@@ -50,6 +52,23 @@ def name(self):
5052
def __unicode__(self):
5153
return self.title or u"<no title>"
5254

55+
@property
56+
def submitted(self):
57+
if getattr(self, '_submitted', None):
58+
return self._submitted
59+
event = self.liaisonstatementevent_set.filter(type__slug='submit')
60+
if event.count():
61+
return event[0].time
62+
return None
63+
64+
@property
65+
def approved(self):
66+
return self.state_id == 'approved'
67+
68+
@property
69+
def action_taken(self):
70+
return bool(self.tags.filter(slug='taken').count())
71+
5372

5473
class LiaisonStatementAttachments(models.Model):
5574
statement = models.ForeignKey(LiaisonStatement)

ietf/liaisons/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities")
1414

1515
def approvable_liaison_statements(user):
16-
liaisons = LiaisonStatement.objects.filter(approved=None)
16+
liaisons = LiaisonStatement.objects.filter(state__slug='pending')
1717
if has_role(user, "Secretariat"):
1818
return liaisons
1919

@@ -28,7 +28,7 @@ def approvable_liaison_statements(user):
2828
else:
2929
group_acronyms.append(x)
3030

31-
return liaisons.filter(Q(from_group__acronym__in=group_acronyms) | Q(from_group__pk__in=group_ids))
31+
return liaisons.filter(Q(from_groups__acronym__in=group_acronyms) | Q(from_groups__pk__in=group_ids))
3232

3333

3434
# the following is a biggish object hierarchy abstracting the entity

ietf/liaisons/views.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
can_add_incoming_liaison,
1414
is_ietfchair, is_iabchair, is_iab_executive_director,
1515
can_edit_liaison, is_secretariat)
16-
from ietf.liaisons.forms import liaison_form_factory
16+
from ietf.liaisons.forms import liaison_form_factory, SearchLiaisonForm
1717
from ietf.liaisons.utils import IETFHM, can_submit_liaison_required, approvable_liaison_statements
1818
from ietf.liaisons.mails import notify_pending_by_email, send_liaison_by_email
1919

@@ -85,16 +85,23 @@ def get_info(request):
8585
def normalize_sort(request):
8686
sort = request.GET.get('sort', "")
8787
if sort not in ('submitted', 'deadline', 'title', 'to_name', 'from_name'):
88-
sort = "submitted"
88+
sort = "id"
8989

9090
# reverse dates
9191
order_by = "-" + sort if sort in ("submitted", "deadline") else sort
9292

9393
return sort, order_by
9494

9595
def liaison_list(request):
96-
sort, order_by = normalize_sort(request)
97-
liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by).prefetch_related("attachments")
96+
if request.GET.get('search', None):
97+
form = SearchLiaisonForm(data=request.GET)
98+
if form.is_valid():
99+
result = form.get_results()
100+
else:
101+
form = SearchLiaisonForm()
102+
result = form.get_results()
103+
104+
liaisons = result
98105

99106
can_send_outgoing = can_add_outgoing_liaison(request.user)
100107
can_send_incoming = can_add_incoming_liaison(request.user)
@@ -107,7 +114,8 @@ def liaison_list(request):
107114
"approvable": approvable,
108115
"can_send_incoming": can_send_incoming,
109116
"can_send_outgoing": can_send_outgoing,
110-
"sort": sort,
117+
"with_search": True,
118+
"form": form,
111119
}, context_instance=RequestContext(request))
112120

113121
def ajax_liaison_list(request):
@@ -121,7 +129,7 @@ def ajax_liaison_list(request):
121129

122130
@can_submit_liaison_required
123131
def liaison_approval_list(request):
124-
liaisons = approvable_liaison_statements(request.user).order_by("-submitted")
132+
liaisons = approvable_liaison_statements(request.user).order_by("-id")
125133

126134
return render_to_response('liaisons/approval_list.html', {
127135
"liaisons": liaisons,
@@ -184,7 +192,7 @@ def _find_person_in_emails(liaison, person):
184192

185193

186194
def liaison_detail(request, object_id):
187-
liaison = get_object_or_404(LiaisonStatement.objects.exclude(approved=None), pk=object_id)
195+
liaison = get_object_or_404(LiaisonStatement.objects.filter(state__slug='approved'), pk=object_id)
188196
can_edit = request.user.is_authenticated() and can_edit_liaison(request.user, liaison)
189197
can_take_care = _can_take_care(liaison, request.user)
190198

@@ -193,7 +201,7 @@ def liaison_detail(request, object_id):
193201
liaison.save()
194202
can_take_care = False
195203

196-
relations = liaison.liaisonstatement_set.exclude(approved=None)
204+
relations = liaison.source_of_set.filter(target__state__slug='approved')
197205

198206
return render_to_response("liaisons/detail.html", {
199207
"liaison": liaison,

ietf/templates/liaisons/liaison_table.html

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
{% load ietf_filters %}
22

3-
<table class="ietf-table" width="100%">
3+
<table class="ietf-table" width="100%" id="LiaisonListTable">
4+
<thead>
45
<tr>
5-
<th width="9%" class="sort{% if sort == "submitted" %} sorted{% endif %}"><a href="?sort=submitted">Date</a></th>
6-
<th width="15%" class="sort{% if sort == "from_name" %} sorted{% endif %}"><a href="?sort=from_name">From</a></th>
7-
<th width="15%" class="sort{% if sort == "to_name" %} sorted{% endif %}"><a href="?sort=to_name">To</a></th>
8-
<th width="9%" class="sort{% if sort == "deadline" %} sorted{% endif %}"><a href="?sort=deadline">Deadline</a></th>
9-
<th width="50%" class="sort{% if sort == "title" %} sorted{% endif %}"><a href="?sort=title">Title</a></th>
6+
<th width="9%" class="sort{% if sort == "submitted" %} sorted{% endif %}">Date</th>
7+
<th width="15%" class="sort{% if sort == "from_name" %} sorted{% endif %}">From</th>
8+
<th width="15%" class="sort{% if sort == "to_name" %} sorted{% endif %}">To</th>
9+
<th width="9%" class="sort{% if sort == "deadline" %} sorted{% endif %}">Deadline</th>
10+
<th width="50%" class="sort{% if sort == "title" %} sorted{% endif %}">Title</th>
1011
</tr>
12+
</thead>
1113

14+
<tbody>
1215
{% for liaison in liaisons %}
1316
<tr class="{% cycle oddrow,evenrow %}">
1417
<td style="white-space:nowrap;">{{ liaison.submitted|date:"Y-m-d" }}</td>
15-
<td>{{ liaison.from_name }}</td>
18+
<td>{{ liaison.from_concat|default:liaison.from_name }}</td>
1619
<td>
17-
{% if liaison.from_contact_id %}
18-
{{ liaison.to_name }}
20+
{% if liaison.to_concat %}
21+
{{ liaison.to_concat }}
1922
{% else %}
2023
{{ liaison.to_name|strip_email }}
2124
{% endif %}
@@ -35,5 +38,5 @@
3538
</td>
3639
</tr>
3740
{% endfor %}
38-
41+
</tbody>
3942
</table>

ietf/templates/liaisons/overview.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ <h1>Liaison Statements</h1>
2323
{% endif %}
2424
{% endblock %}
2525

26+
{% if with_search %}
27+
<div class="ietf-box search-form-box">
28+
{% include "liaisons/search_form.html" %}
29+
</div>
30+
{% endif %}
31+
2632
{% include "liaisons/liaison_table.html" %}
2733

2834
{% endblock %}
29-
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<form id="search_form" class="search_form" action="{% url "liaison_list" %}" method="get">
2+
3+
<table>
4+
<tr><td><label>Search text:</label></td><td colspan="3">{{ form.text }}</td></tr>
5+
<tr><td><label>Search text scope:</label></td><td colspan="3">{{ form.scope }}</td></tr>
6+
<tr><td colspan="4">&nbsp;</td></tr>
7+
<tr><td><label>Source:</label></td><td>{{ form.source }}</td><td><label>Destination:</label></td><td>{{ form.destination }}</td></tr>
8+
<tr><td><label>From date:</label><div class="help">{{ form.start_date.help_text }}</div></td><td>{{ form.start_date }}{{ form.errors.start_date }}</td><td><label>To date:</label><div class="help">{{ form.start_date.help_text }}</div></td><td>{{ form.end_date }}{{ form.errors.end_date }}</td></tr>
9+
</table>
10+
11+
<div class="submit">
12+
<input type="submit" class="button" name="search" value="Search"/>
13+
</div>
14+
15+
</form>

0 commit comments

Comments
 (0)