Search form for liaison statement list. See #1434

- Legacy-Id: 8049
This commit is contained in:
Emilio A. Sánchez López 2014-07-04 09:45:13 +00:00
parent f72b0a1c93
commit abb74d9977
7 changed files with 138 additions and 22 deletions

View file

@ -4,15 +4,21 @@ from email.utils import parseaddr
from django import forms
from django.conf import settings
from django.forms.util import ErrorList
from django.db.models import Q
from django.forms.widgets import RadioFieldRenderer
from django.core.validators import validate_email, ValidationError
from django.template.loader import render_to_string
from django.utils.html import format_html
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from ietf.liaisons.accounts import (can_add_outgoing_liaison, can_add_incoming_liaison,
get_person_for_user, is_secretariat, is_sdo_liaison_manager)
from ietf.liaisons.utils import IETFHM
from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget,
ShowAttachmentsWidget, RelatedLiaisonWidget)
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName, LiaisonStatementEvent
from ietf.group.models import Group, Role
from ietf.person.models import Person, Email
from ietf.doc.models import Document
@ -468,3 +474,63 @@ def liaison_form_factory(request, **kwargs):
return IncomingLiaisonForm(user, **kwargs)
return None
class RadioRenderer(RadioFieldRenderer):
def render(self):
output = []
for widget in self:
output.append(format_html(force_text(widget)))
return mark_safe('\n'.join(output))
class SearchLiaisonForm(forms.Form):
text = forms.CharField(required=False)
scope = forms.ChoiceField(choices=(("all", "All text fields"), ("title", "Title field")), required=False, initial='title', widget=forms.RadioSelect(renderer=RadioRenderer))
source = forms.CharField(required=False)
destination = forms.CharField(required=False)
start_date = forms.DateField(required=False, help_text="Format: YYYY-MM-DD")
end_date = forms.DateField(required=False, help_text="Format: YYYY-MM-DD")
def get_results(self):
results = LiaisonStatement.objects.filter(state__slug='approved').extra(
select={
'_submitted': 'SELECT time FROM liaisons_liaisonstatementevent WHERE liaisons_liaisonstatement.id = liaisons_liaisonstatementevent.statement_id AND liaisons_liaisonstatementevent.type_id = "submit"',
'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',
'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',
})
if self.is_bound:
query = self.cleaned_data.get('text')
if query:
if self.cleaned_data.get('scope') == 'title':
q = Q(title__icontains=query)
else:
q = (Q(title__icontains=query) | Q(other_identifiers__icontains=query) | Q(body__icontains=query) | Q(attachments__title__icontains=query) |
Q(response_contacts__icontains=query) | Q(technical_contacts__icontains=query) | Q(action_holder_contacts__icontains=query) |
Q(cc_contacts=query))
results = results.filter(q)
source = self.cleaned_data.get('source')
if source:
results = results.filter(Q(from_groups__name__icontains=source) | Q(from_groups__acronym__iexact=source) | Q(from_name__icontains=source))
destination = self.cleaned_data.get('destination')
if destination:
results = results.filter(Q(to_groups__name__icontains=destination) | Q(to_groups__acronym__iexact=destination) | Q(to_name__icontains=destination))
start_date = self.cleaned_data.get('start_date')
end_date = self.cleaned_data.get('end_date')
events = None
if start_date:
events = LiaisonStatementEvent.objects.filter(type='submit', time__gte=start_date)
if end_date:
events = events.filter(time__lte=end_date)
elif end_date:
events = LiaisonStatementEvent.objects.filter(type='submit', time__lte=end_date)
if events:
results = results.filter(liaisonstatementevent__in=events)
destination = self.cleaned_data.get('destination')
if destination:
results = results.filter(Q(to_groups__name__icontains=destination) | Q(to_groups__acronym__iexact=destination) | Q(to_name__icontains=destination))
results = results.distinct().order_by('title')
return results

View file

@ -36,6 +36,8 @@ class LiaisonStatement(models.Model):
attachments = models.ManyToManyField(Document, through='LiaisonStatementAttachments', blank=True)
state = models.ForeignKey(LiaisonStatementState, default='pending')
def name(self):
if self.from_group:
frm = self.from_group.acronym or self.from_group.name
@ -50,6 +52,23 @@ class LiaisonStatement(models.Model):
def __unicode__(self):
return self.title or u"<no title>"
@property
def submitted(self):
if getattr(self, '_submitted', None):
return self._submitted
event = self.liaisonstatementevent_set.filter(type__slug='submit')
if event.count():
return event[0].time
return None
@property
def approved(self):
return self.state_id == 'approved'
@property
def action_taken(self):
return bool(self.tags.filter(slug='taken').count())
class LiaisonStatementAttachments(models.Model):
statement = models.ForeignKey(LiaisonStatement)

View file

@ -13,7 +13,7 @@ can_submit_liaison_required = passes_test_decorator(
"Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities")
def approvable_liaison_statements(user):
liaisons = LiaisonStatement.objects.filter(approved=None)
liaisons = LiaisonStatement.objects.filter(state__slug='pending')
if has_role(user, "Secretariat"):
return liaisons
@ -28,7 +28,7 @@ def approvable_liaison_statements(user):
else:
group_acronyms.append(x)
return liaisons.filter(Q(from_group__acronym__in=group_acronyms) | Q(from_group__pk__in=group_ids))
return liaisons.filter(Q(from_groups__acronym__in=group_acronyms) | Q(from_groups__pk__in=group_ids))
# the following is a biggish object hierarchy abstracting the entity

View file

@ -13,7 +13,7 @@ from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaiso
can_add_incoming_liaison,
is_ietfchair, is_iabchair, is_iab_executive_director,
can_edit_liaison, is_secretariat)
from ietf.liaisons.forms import liaison_form_factory
from ietf.liaisons.forms import liaison_form_factory, SearchLiaisonForm
from ietf.liaisons.utils import IETFHM, can_submit_liaison_required, approvable_liaison_statements
from ietf.liaisons.mails import notify_pending_by_email, send_liaison_by_email
@ -85,7 +85,7 @@ def get_info(request):
def normalize_sort(request):
sort = request.GET.get('sort', "")
if sort not in ('submitted', 'deadline', 'title', 'to_name', 'from_name'):
sort = "submitted"
sort = "id"
# reverse dates
order_by = "-" + sort if sort in ("submitted", "deadline") else sort
@ -93,8 +93,15 @@ def normalize_sort(request):
return sort, order_by
def liaison_list(request):
sort, order_by = normalize_sort(request)
liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by).prefetch_related("attachments")
if request.GET.get('search', None):
form = SearchLiaisonForm(data=request.GET)
if form.is_valid():
result = form.get_results()
else:
form = SearchLiaisonForm()
result = form.get_results()
liaisons = result
can_send_outgoing = can_add_outgoing_liaison(request.user)
can_send_incoming = can_add_incoming_liaison(request.user)
@ -107,7 +114,8 @@ def liaison_list(request):
"approvable": approvable,
"can_send_incoming": can_send_incoming,
"can_send_outgoing": can_send_outgoing,
"sort": sort,
"with_search": True,
"form": form,
}, context_instance=RequestContext(request))
def ajax_liaison_list(request):
@ -121,7 +129,7 @@ def ajax_liaison_list(request):
@can_submit_liaison_required
def liaison_approval_list(request):
liaisons = approvable_liaison_statements(request.user).order_by("-submitted")
liaisons = approvable_liaison_statements(request.user).order_by("-id")
return render_to_response('liaisons/approval_list.html', {
"liaisons": liaisons,
@ -184,7 +192,7 @@ def _find_person_in_emails(liaison, person):
def liaison_detail(request, object_id):
liaison = get_object_or_404(LiaisonStatement.objects.exclude(approved=None), pk=object_id)
liaison = get_object_or_404(LiaisonStatement.objects.filter(state__slug='approved'), pk=object_id)
can_edit = request.user.is_authenticated() and can_edit_liaison(request.user, liaison)
can_take_care = _can_take_care(liaison, request.user)
@ -193,7 +201,7 @@ def liaison_detail(request, object_id):
liaison.save()
can_take_care = False
relations = liaison.liaisonstatement_set.exclude(approved=None)
relations = liaison.source_of_set.filter(target__state__slug='approved')
return render_to_response("liaisons/detail.html", {
"liaison": liaison,

View file

@ -1,21 +1,24 @@
{% load ietf_filters %}
<table class="ietf-table" width="100%">
<table class="ietf-table" width="100%" id="LiaisonListTable">
<thead>
<tr>
<th width="9%" class="sort{% if sort == "submitted" %} sorted{% endif %}"><a href="?sort=submitted">Date</a></th>
<th width="15%" class="sort{% if sort == "from_name" %} sorted{% endif %}"><a href="?sort=from_name">From</a></th>
<th width="15%" class="sort{% if sort == "to_name" %} sorted{% endif %}"><a href="?sort=to_name">To</a></th>
<th width="9%" class="sort{% if sort == "deadline" %} sorted{% endif %}"><a href="?sort=deadline">Deadline</a></th>
<th width="50%" class="sort{% if sort == "title" %} sorted{% endif %}"><a href="?sort=title">Title</a></th>
<th width="9%" class="sort{% if sort == "submitted" %} sorted{% endif %}">Date</th>
<th width="15%" class="sort{% if sort == "from_name" %} sorted{% endif %}">From</th>
<th width="15%" class="sort{% if sort == "to_name" %} sorted{% endif %}">To</th>
<th width="9%" class="sort{% if sort == "deadline" %} sorted{% endif %}">Deadline</th>
<th width="50%" class="sort{% if sort == "title" %} sorted{% endif %}">Title</th>
</tr>
</thead>
<tbody>
{% for liaison in liaisons %}
<tr class="{% cycle oddrow,evenrow %}">
<td style="white-space:nowrap;">{{ liaison.submitted|date:"Y-m-d" }}</td>
<td>{{ liaison.from_name }}</td>
<td>{{ liaison.from_concat|default:liaison.from_name }}</td>
<td>
{% if liaison.from_contact_id %}
{{ liaison.to_name }}
{% if liaison.to_concat %}
{{ liaison.to_concat }}
{% else %}
{{ liaison.to_name|strip_email }}
{% endif %}
@ -35,5 +38,5 @@
</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -23,7 +23,12 @@
{% endif %}
{% endblock %}
{% if with_search %}
<div class="ietf-box search-form-box">
{% include "liaisons/search_form.html" %}
</div>
{% endif %}
{% include "liaisons/liaison_table.html" %}
{% endblock %}

View file

@ -0,0 +1,15 @@
<form id="search_form" class="search_form" action="{% url "liaison_list" %}" method="get">
<table>
<tr><td><label>Search text:</label></td><td colspan="3">{{ form.text }}</td></tr>
<tr><td><label>Search text scope:</label></td><td colspan="3">{{ form.scope }}</td></tr>
<tr><td colspan="4">&nbsp;</td></tr>
<tr><td><label>Source:</label></td><td>{{ form.source }}</td><td><label>Destination:</label></td><td>{{ form.destination }}</td></tr>
<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>
</table>
<div class="submit">
<input type="submit" class="button" name="search" value="Search"/>
</div>
</form>