Commit 0de20def88c698cd8a23188dc1eb0ac043e6427c
Exists in
master
and in
39 other branches
Merge branch 'haystack'
Showing
6 changed files
with
219 additions
and
78 deletions
Show diff stats
src/search/forms.py
@@ -8,13 +8,35 @@ from django.utils.translation import ugettext_lazy as _ | @@ -8,13 +8,35 @@ from django.utils.translation import ugettext_lazy as _ | ||
8 | from haystack.forms import SearchForm | 8 | from haystack.forms import SearchForm |
9 | 9 | ||
10 | from accounts.models import User | 10 | from accounts.models import User |
11 | -from super_archives.models import Message | 11 | +from super_archives.models import Message, MailingList |
12 | 12 | ||
13 | 13 | ||
14 | class ColabSearchForm(SearchForm): | 14 | class ColabSearchForm(SearchForm): |
15 | q = forms.CharField(label=_('Search'), required=False) | 15 | q = forms.CharField(label=_('Search'), required=False) |
16 | order = forms.CharField(widget=forms.HiddenInput(), required=False) | 16 | order = forms.CharField(widget=forms.HiddenInput(), required=False) |
17 | type = forms.CharField(required=False, label=_(u'Type')) | 17 | type = forms.CharField(required=False, label=_(u'Type')) |
18 | + author = forms.CharField(required=False, label=_(u'Author')) | ||
19 | + # ticket status | ||
20 | + tag = forms.CharField(required=False, label=_(u'Status')) | ||
21 | + # mailinglist tag | ||
22 | + list = forms.MultipleChoiceField( | ||
23 | + required=False, | ||
24 | + label=_(u'Mailinglist'), | ||
25 | + choices=[(v, v) for v in MailingList.objects.values('name') | ||
26 | + for (v, v) in v.items()] | ||
27 | + ) | ||
28 | + milestone = forms.CharField(required=False, label=_(u'Milestone')) | ||
29 | + priority = forms.CharField(required=False, label=_(u'Priority')) | ||
30 | + component = forms.CharField(required=False, label=_(u'Component')) | ||
31 | + severity = forms.CharField(required=False, label=_(u'Severity')) | ||
32 | + reporter = forms.CharField(required=False, label=_(u'Reporter')) | ||
33 | + keywords = forms.CharField(required=False, label=_(u'Keywords')) | ||
34 | + collaborators = forms.CharField(required=False, label=_(u'Collaborators')) | ||
35 | + repository_name = forms.CharField(required=False, label=_(u'Repository')) | ||
36 | + username = forms.CharField(required=False, label=_(u'Username')) | ||
37 | + name = forms.CharField(required=False, label=_(u'Name')) | ||
38 | + institution = forms.CharField(required=False, label=_(u'Institution')) | ||
39 | + role = forms.CharField(required=False, label=_(u'Role')) | ||
18 | 40 | ||
19 | def search(self): | 41 | def search(self): |
20 | if not self.is_valid(): | 42 | if not self.is_valid(): |
@@ -33,25 +55,44 @@ class ColabSearchForm(SearchForm): | @@ -33,25 +55,44 @@ class ColabSearchForm(SearchForm): | ||
33 | types = self.cleaned_data['type'] | 55 | types = self.cleaned_data['type'] |
34 | sqs = sqs.filter(type__in=types.split()) | 56 | sqs = sqs.filter(type__in=types.split()) |
35 | 57 | ||
36 | - | ||
37 | if self.cleaned_data['order']: | 58 | if self.cleaned_data['order']: |
38 | for option, dict_order in settings.ORDERING_DATA.items(): | 59 | for option, dict_order in settings.ORDERING_DATA.items(): |
39 | if self.cleaned_data['order'] == option: | 60 | if self.cleaned_data['order'] == option: |
40 | if dict_order['fields']: | 61 | if dict_order['fields']: |
41 | sqs = sqs.order_by(*dict_order['fields']) | 62 | sqs = sqs.order_by(*dict_order['fields']) |
42 | - # if self.cleaned_data['type'] == 'user': | ||
43 | - # sqs = self.searchqueryset.models(User) | ||
44 | - # elif self.cleaned_data['type'] in ['message', 'thread']: | ||
45 | - # sqs = self.searchqueryset.models(Message) | ||
46 | - # elif self.cleaned_data['type'] == 'wiki': | ||
47 | - # sqs = self.searchqueryset.models(Wiki) | ||
48 | - # elif self.cleaned_data['type'] == 'changeset': | ||
49 | - # sqs = self.searchqueryset.models(Changeset) | ||
50 | - # elif self.cleaned_data['type'] == 'ticket': | ||
51 | - # sqs = self.searchqueryset.models(Ticket) | ||
52 | - # else: | ||
53 | - # sqs = self.searchqueryset.all() | ||
54 | 63 | ||
64 | + if self.cleaned_data['author']: | ||
65 | + sqs = sqs.filter(author=self.cleaned_data['author']) | ||
66 | + | ||
67 | + if self.cleaned_data['milestone']: | ||
68 | + sqs = sqs.filter(milestone=self.cleaned_data['milestone']) | ||
69 | + if self.cleaned_data['priority']: | ||
70 | + sqs = sqs.filter(priority=self.cleaned_data['priority']) | ||
71 | + if self.cleaned_data['severity']: | ||
72 | + sqs = sqs.filter(severity=self.cleaned_data['severity']) | ||
73 | + if self.cleaned_data['reporter']: | ||
74 | + sqs = sqs.filter(reporter=self.cleaned_data['reporter']) | ||
75 | + if self.cleaned_data['keywords']: | ||
76 | + sqs = sqs.filter(keywords=self.cleaned_data['keywords']) | ||
77 | + if self.cleaned_data['collaborators']: | ||
78 | + sqs = sqs.filter(collaborators=self.cleaned_data['collaborators']) | ||
79 | + if self.cleaned_data['repository_name']: | ||
80 | + sqs = sqs.filter( | ||
81 | + repository_name=self.cleaned_data['repository_name'] | ||
82 | + ) | ||
83 | + if self.cleaned_data['username']: | ||
84 | + sqs = sqs.filter(username=self.cleaned_data['username']) | ||
85 | + if self.cleaned_data['name']: | ||
86 | + sqs = sqs.filter(name=self.cleaned_data['name']) | ||
87 | + if self.cleaned_data['institution']: | ||
88 | + sqs = sqs.filter(institution=self.cleaned_data['institution']) | ||
89 | + if self.cleaned_data['role']: | ||
90 | + sqs = sqs.filter(role=self.cleaned_data['role']) | ||
91 | + if self.cleaned_data['tag']: | ||
92 | + sqs = sqs.filter(tag=self.cleaned_data['tag']) | ||
93 | + | ||
94 | + if self.cleaned_data['list']: | ||
95 | + sqs = sqs.filter(tag__in=self.cleaned_data['list']) | ||
55 | 96 | ||
56 | if self.load_all: | 97 | if self.load_all: |
57 | sqs = sqs.load_all() | 98 | sqs = sqs.load_all() |
src/search/views.py
1 | # -*- coding:utf-8 -*- | 1 | # -*- coding:utf-8 -*- |
2 | 2 | ||
3 | from django.conf import settings | 3 | from django.conf import settings |
4 | +from django.utils.translation import ugettext as _ | ||
5 | + | ||
4 | from haystack.views import SearchView | 6 | from haystack.views import SearchView |
5 | 7 | ||
6 | 8 | ||
7 | class ColabSearchView(SearchView): | 9 | class ColabSearchView(SearchView): |
8 | def extra_context(self, *args, **kwargs): | 10 | def extra_context(self, *args, **kwargs): |
9 | - # Retornar todos os campos de cada tipo a serem filtrados | ||
10 | - # retornar os nomes dos campos | ||
11 | - # retornar os ícones dos tipos | ||
12 | - | ||
13 | - # a critical point on the system | ||
14 | types = { | 11 | types = { |
15 | 'wiki': { | 12 | 'wiki': { |
16 | 'icon': 'file', | 13 | 'icon': 'file', |
17 | - 'fields': [ | ||
18 | - 'title', 'description', 'author', 'collaborators', | ||
19 | - 'created', 'modified', | ||
20 | - ], | 14 | + 'name': _(u'Wiki'), |
15 | + 'fields': ( | ||
16 | + ('author', _(u'Author'), self.request.GET.get('author')), | ||
17 | + ), | ||
21 | }, | 18 | }, |
22 | - 'discussion': { | 19 | + 'thread': { |
23 | 'icon': 'thread', | 20 | 'icon': 'thread', |
24 | - 'fields': [ | ||
25 | - 'title', 'description', 'created', 'modified', 'author', | ||
26 | - 'tag', | ||
27 | - ], | 21 | + 'name': _(u'Discussion'), |
22 | + 'fields': ( | ||
23 | + ('author', _(u'Author'), self.request.GET.get('author')), | ||
24 | + ( | ||
25 | + 'list', | ||
26 | + _(u'Mailinglist'), | ||
27 | + self.request.GET.getlist('list') | ||
28 | + ), | ||
29 | + ), | ||
28 | }, | 30 | }, |
29 | 'ticket': { | 31 | 'ticket': { |
30 | 'icon': 'ticket', | 32 | 'icon': 'ticket', |
31 | - 'fields': [ | ||
32 | - 'title', 'description', 'milestone', 'priority', | ||
33 | - 'component', 'version', 'severity', 'reporter', 'author', | ||
34 | - 'status', 'keywords', 'collaborators', 'created', | ||
35 | - 'modified', | ||
36 | - ], | 33 | + 'name': _(u'Ticket'), |
34 | + 'fields': ( | ||
35 | + ( | ||
36 | + 'milestone', | ||
37 | + _(u'Milestone'), | ||
38 | + self.request.GET.get('milestone') | ||
39 | + ), | ||
40 | + ( | ||
41 | + 'priority', | ||
42 | + _(u'Priority'), | ||
43 | + self.request.GET.get('priority') | ||
44 | + ), | ||
45 | + ( | ||
46 | + 'component', | ||
47 | + _(u'Component'), | ||
48 | + self.request.GET.get('component') | ||
49 | + ), | ||
50 | + ( | ||
51 | + 'severity', | ||
52 | + _(u'Severity'), | ||
53 | + self.request.GET.get('severity') | ||
54 | + ), | ||
55 | + ( | ||
56 | + 'reporter', | ||
57 | + _(u'Reporter'), | ||
58 | + self.request.GET.get('reporter') | ||
59 | + ), | ||
60 | + ('author', _(u'Author'), self.request.GET.get('author')), | ||
61 | + ('tag', _(u'Status'), self.request.GET.get('tag')), | ||
62 | + ( | ||
63 | + 'keywords', | ||
64 | + _(u'Keywords'), | ||
65 | + self.request.GET.get('keywords'), | ||
66 | + ), | ||
67 | + ( | ||
68 | + 'collaborators', | ||
69 | + _(u'Collaborators'), | ||
70 | + self.request.GET.get('collaborators') | ||
71 | + ), | ||
72 | + ), | ||
37 | }, | 73 | }, |
38 | 'changeset': { | 74 | 'changeset': { |
39 | 'icon': 'changeset', | 75 | 'icon': 'changeset', |
40 | - 'fields': [ | ||
41 | - 'title', 'author', 'description', 'repository_name', | ||
42 | - 'created', 'modified', | ||
43 | - ], | 76 | + 'name': _(u'Changeset'), |
77 | + 'fields': ( | ||
78 | + ('author', _(u'Author'), self.request.GET.get('author')), | ||
79 | + ( | ||
80 | + 'repository_name', | ||
81 | + _(u'Repository'), | ||
82 | + self.request.GET.get('repository_name'), | ||
83 | + ), | ||
84 | + ) | ||
44 | }, | 85 | }, |
45 | 'user': { | 86 | 'user': { |
46 | 'icon': 'user', | 87 | 'icon': 'user', |
47 | - 'fields': [ | ||
48 | - 'title', 'description', 'username', 'name', | ||
49 | - 'email', 'institution', 'role', 'google_talk', 'webpage', | ||
50 | - ], | 88 | + 'name': _(u'User'), |
89 | + 'fields': ( | ||
90 | + ( | ||
91 | + 'username', | ||
92 | + _(u'Username'), | ||
93 | + self.request.GET.get('username'), | ||
94 | + ), | ||
95 | + ('name', _(u'Name'), self.request.GET.get('name')), | ||
96 | + ( | ||
97 | + 'institution', | ||
98 | + _(u'Institution'), | ||
99 | + self.request.GET.get('institution'), | ||
100 | + ), | ||
101 | + ('role', _(u'Role'), self.request.GET.get('role')) | ||
102 | + ), | ||
51 | }, | 103 | }, |
52 | } | 104 | } |
53 | - types = self.form.cleaned_data['type'] | 105 | + |
106 | + try: | ||
107 | + type_chosen = self.form.cleaned_data.get('type') | ||
108 | + except AttributeError: | ||
109 | + type_chosen = '' | ||
110 | + | ||
54 | return dict( | 111 | return dict( |
55 | - types=types.split(), | ||
56 | - types_str=types, | 112 | + filters=types.get(type_chosen), |
113 | + type_chosen=type_chosen, | ||
57 | order_data=settings.ORDERING_DATA | 114 | order_data=settings.ORDERING_DATA |
58 | ) | 115 | ) |
src/super_archives/templates/message-list.html
@@ -27,9 +27,12 @@ | @@ -27,9 +27,12 @@ | ||
27 | <h4>{% trans "Lists" %}</h4> | 27 | <h4>{% trans "Lists" %}</h4> |
28 | <ul class="unstyled-list"> | 28 | <ul class="unstyled-list"> |
29 | {% for list in lists %} | 29 | {% for list in lists %} |
30 | - <li {% if list.name == selected_list %} title="{% trans "Remove filter" %}" class="selected" {% endif %}> | ||
31 | - <span class="glyphicon {% if list.name == selected_list %}glyphicon-remove{% else %}glyphicon-chevron-right{% endif %}"></span> <a href="{% ifnotequal list.name selected_list %} {% append_to_get list=list.name p=1 %} {% else %} {% append_to_get list="" p=1 %} | ||
32 | - {% endifnotequal %}">{{ list.name }}</a></li> | 30 | + {% with list.name|add:" "|add:selected_lists as list_name %} |
31 | + <li {% if list.name in selected_lists %} title="{% trans "Remove filter" %}" class="selected" {% endif %}> | ||
32 | + <span class="glyphicon {% if list.name in selected_lists %}glyphicon-remove{% else %}glyphicon-chevron-right{% endif %}"></span> | ||
33 | + <a href="{% if not list.name in selected_lists %}{% append_to_get list=list_name p=1 %}{% else %}{% pop_from_get list=list.name %}{% endif %}">{{ list.name }}</a> | ||
34 | + </li> | ||
35 | + {% endwith %} | ||
33 | {% endfor %} | 36 | {% endfor %} |
34 | </ul> | 37 | </ul> |
35 | </div> | 38 | </div> |
src/super_archives/utils/url.py
@@ -19,6 +19,7 @@ def pop_from_get(path, query=None, **kwargs): | @@ -19,6 +19,7 @@ def pop_from_get(path, query=None, **kwargs): | ||
19 | if query_dict[key] == value: | 19 | if query_dict[key] == value: |
20 | del query_dict[key] | 20 | del query_dict[key] |
21 | continue | 21 | continue |
22 | - if not query_dict: | ||
23 | - return u'{}?q='.format(path) | 22 | + if value in query_dict[key]: |
23 | + aux = query_dict[key].split(value) | ||
24 | + query_dict[key] = u''.join(aux).strip() | ||
24 | return u'{}?{}'.format(path, urllib.urlencode(query_dict)) | 25 | return u'{}?{}'.format(path, urllib.urlencode(query_dict)) |
src/super_archives/views.py
@@ -60,8 +60,9 @@ def thread(request, mailinglist, thread_token): | @@ -60,8 +60,9 @@ def thread(request, mailinglist, thread_token): | ||
60 | 60 | ||
61 | 61 | ||
62 | def list_messages(request): | 62 | def list_messages(request): |
63 | - | ||
64 | - selected_list = request.GET.get('list') | 63 | + selected_lists = request.GET.get('list', []) |
64 | + if selected_lists: | ||
65 | + selected_lists = selected_lists.split() | ||
65 | 66 | ||
66 | order_by = request.GET.get('order') | 67 | order_by = request.GET.get('order') |
67 | if order_by == 'hottest': | 68 | if order_by == 'hottest': |
@@ -69,9 +70,9 @@ def list_messages(request): | @@ -69,9 +70,9 @@ def list_messages(request): | ||
69 | else: | 70 | else: |
70 | threads = queries.get_latest_threads() | 71 | threads = queries.get_latest_threads() |
71 | 72 | ||
72 | - mail_list = request.GET.get('list') | 73 | + mail_list = selected_lists |
73 | if mail_list: | 74 | if mail_list: |
74 | - threads = threads.filter(mailinglist__name=mail_list) | 75 | + threads = threads.filter(mailinglist__name__in=mail_list) |
75 | 76 | ||
76 | paginator = Paginator(threads, 16) | 77 | paginator = Paginator(threads, 16) |
77 | try: | 78 | try: |
@@ -86,7 +87,7 @@ def list_messages(request): | @@ -86,7 +87,7 @@ def list_messages(request): | ||
86 | 'lists': lists, | 87 | 'lists': lists, |
87 | 'n_results': paginator.count, | 88 | 'n_results': paginator.count, |
88 | 'threads': threads, | 89 | 'threads': threads, |
89 | - 'selected_list': selected_list, | 90 | + 'selected_lists': ' '.join(selected_lists) if selected_lists else '', |
90 | 'order_data': settings.ORDERING_DATA, | 91 | 'order_data': settings.ORDERING_DATA, |
91 | } | 92 | } |
92 | return render(request, 'message-list.html', template_data) | 93 | return render(request, 'message-list.html', template_data) |
src/templates/search/search.html
@@ -17,6 +17,42 @@ | @@ -17,6 +17,42 @@ | ||
17 | <div id="filters" class="hidden-xs hidden-sm col-md-2 col-lg-2"> | 17 | <div id="filters" class="hidden-xs hidden-sm col-md-2 col-lg-2"> |
18 | <h3>{% trans "Filters" %}</h3> | 18 | <h3>{% trans "Filters" %}</h3> |
19 | 19 | ||
20 | + {% if filters %} | ||
21 | + <ul class="unstyled-list"> | ||
22 | + <li class="selected" title="{% trans "Remove filter" %}"> | ||
23 | + <span class="glyphicon glyphicon-remove"></span> | ||
24 | + <a href="{% url 'haystack_search' %}?q={{ request.GET.q }}"> | ||
25 | + <span class="glyphicon glyphicon-{{ filters.icon }}"></span> | ||
26 | + {{ filters.name }} | ||
27 | + </a> | ||
28 | + </li> | ||
29 | + </ul> | ||
30 | + <hr /> | ||
31 | + <form role="form"> | ||
32 | + <input type="hidden" name="q" value="{{ request.GET.q }}" /> | ||
33 | + <input type="hidden" name="type" value="{{ type_chosen }}" /> | ||
34 | + | ||
35 | + {% for field_lookup, field_display, field_value in filters.fields %} | ||
36 | + <div class="form-group"> | ||
37 | + <label for="{{ field_lookup }}">{{ field_display }}</label> | ||
38 | + {% ifequal field_lookup "list" %} | ||
39 | + <select name="{{ field_lookup }}" class="form-control" multiple> | ||
40 | + {% for value, option in form.fields.list.choices %} | ||
41 | + <option value="{{ value }}" {% if value in field_value %}selected{% endif %}>{{ option }}</option> | ||
42 | + {% endfor %} | ||
43 | + </select> | ||
44 | + {% else %} | ||
45 | + <div class="input-group"> | ||
46 | + <input type="text" class="form-control" placeholder="{{ field_display }}" name="{{ field_lookup }}" {% if field_value %}value="{{ field_value }}"{% endif %}> | ||
47 | + </div> | ||
48 | + {% endifequal %} | ||
49 | + </div> | ||
50 | + {% endfor %} | ||
51 | + <input type="submit" class="btn btn-default pull-right" value="{% trans 'Filter' %}" /> | ||
52 | + </form> | ||
53 | + <br /><hr /> | ||
54 | + {% endif %} | ||
55 | + | ||
20 | <h4>{% trans "Sort by" %}</h4> | 56 | <h4>{% trans "Sort by" %}</h4> |
21 | <ul class="unstyled-list"> | 57 | <ul class="unstyled-list"> |
22 | {% for option, dict_order in order_data.items %} | 58 | {% for option, dict_order in order_data.items %} |
@@ -33,30 +69,32 @@ | @@ -33,30 +69,32 @@ | ||
33 | {% endfor %} | 69 | {% endfor %} |
34 | </ul> | 70 | </ul> |
35 | 71 | ||
36 | - <h4>{% trans "Types" %}</h4> | 72 | + {% if not request.GET.type %} |
73 | + <h4>{% trans "Types" %}</h4> | ||
37 | 74 | ||
38 | - <ul class="unstyled-list"> | ||
39 | - <li> | ||
40 | - <span class="glyphicon glyphicon-file"></span> | ||
41 | - <a href="{% append_to_get type='wiki' %}">{% trans "Wiki" %}</a> | ||
42 | - </li> | ||
43 | - <li> | ||
44 | - <span class="glyphicon glyphicon-envelope"></span> | ||
45 | - <a href="{% append_to_get type='thread' %}">{% trans "Discussion" %}</a> | ||
46 | - </li> | ||
47 | - <li> | ||
48 | - <span class="glyphicon glyphicon-tag"></span> | ||
49 | - <a href="{% append_to_get type='ticket' %}">{% trans "Ticket" %}</a> | ||
50 | - </li> | ||
51 | - <li> | ||
52 | - <span class="glyphicon glyphicon-align-right"></span> | ||
53 | - <a href="{% append_to_get type='changeset' %}">{% trans "Changeset" %}</a> | ||
54 | - </li> | ||
55 | - <li> | ||
56 | - <span class="glyphicon glyphicon-user"></span> | ||
57 | - <a href="{% append_to_get type='user' %}">{% trans "User" %}</a> | ||
58 | - </li> | ||
59 | - </ul> | 75 | + <ul class="unstyled-list"> |
76 | + <li> | ||
77 | + <span class="glyphicon glyphicon-file"></span> | ||
78 | + <a href="{% append_to_get type='wiki' %}">{% trans "Wiki" %}</a> | ||
79 | + </li> | ||
80 | + <li> | ||
81 | + <span class="glyphicon glyphicon-envelope"></span> | ||
82 | + <a href="{% append_to_get type='thread' %}">{% trans "Discussion" %}</a> | ||
83 | + </li> | ||
84 | + <li> | ||
85 | + <span class="glyphicon glyphicon-tag"></span> | ||
86 | + <a href="{% append_to_get type='ticket' %}">{% trans "Ticket" %}</a> | ||
87 | + </li> | ||
88 | + <li> | ||
89 | + <span class="glyphicon glyphicon-align-right"></span> | ||
90 | + <a href="{% append_to_get type='changeset' %}">{% trans "Changeset" %}</a> | ||
91 | + </li> | ||
92 | + <li> | ||
93 | + <span class="glyphicon glyphicon-user"></span> | ||
94 | + <a href="{% append_to_get type='user' %}">{% trans "User" %}</a> | ||
95 | + </li> | ||
96 | + </ul> | ||
97 | + {% endif %} | ||
60 | </div> | 98 | </div> |
61 | 99 | ||
62 | <div class="col-lg-10"> | 100 | <div class="col-lg-10"> |