diff --git a/ietf/liaisons/forms.py b/ietf/liaisons/forms.py index 14bf099a4..fcb77862e 100644 --- a/ietf/liaisons/forms.py +++ b/ietf/liaisons/forms.py @@ -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 diff --git a/ietf/liaisons/models.py b/ietf/liaisons/models.py index addafe418..572d1488f 100644 --- a/ietf/liaisons/models.py +++ b/ietf/liaisons/models.py @@ -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"" + @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) diff --git a/ietf/liaisons/utils.py b/ietf/liaisons/utils.py index 91a4a10ee..ee9148ef6 100644 --- a/ietf/liaisons/utils.py +++ b/ietf/liaisons/utils.py @@ -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 diff --git a/ietf/liaisons/views.py b/ietf/liaisons/views.py index c197616a2..aeea5f566 100644 --- a/ietf/liaisons/views.py +++ b/ietf/liaisons/views.py @@ -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, diff --git a/ietf/templates/liaisons/liaison_table.html b/ietf/templates/liaisons/liaison_table.html index fdc18f5a7..ca03884f7 100644 --- a/ietf/templates/liaisons/liaison_table.html +++ b/ietf/templates/liaisons/liaison_table.html @@ -1,21 +1,24 @@ {% load ietf_filters %} - +
+ - - - - - + + + + + + + {% for liaison in liaisons %} - + {% endfor %} - +
DateFromToDeadlineTitleDateFromToDeadlineTitle
{{ liaison.submitted|date:"Y-m-d" }}{{ liaison.from_name }}{{ liaison.from_concat|default:liaison.from_name }} - {% 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 @@
diff --git a/ietf/templates/liaisons/overview.html b/ietf/templates/liaisons/overview.html index 60eaae641..786930788 100644 --- a/ietf/templates/liaisons/overview.html +++ b/ietf/templates/liaisons/overview.html @@ -23,7 +23,12 @@ {% endif %} {% endblock %} +{% if with_search %} +
+{% include "liaisons/search_form.html" %} +
+{% endif %} + {% include "liaisons/liaison_table.html" %} {% endblock %} - diff --git a/ietf/templates/liaisons/search_form.html b/ietf/templates/liaisons/search_form.html new file mode 100644 index 000000000..2c16851c8 --- /dev/null +++ b/ietf/templates/liaisons/search_form.html @@ -0,0 +1,15 @@ +
+ + + + + + + +
{{ form.text }}
{{ form.scope }}
 
{{ form.source }}{{ form.destination }}
{{ form.start_date.help_text }}
{{ form.start_date }}{{ form.errors.start_date }}
{{ form.start_date.help_text }}
{{ form.end_date }}{{ form.errors.end_date }}
+ +
+ +
+ +