Commit f8cb238777b5a8945c115fa5c3d108cdf2e773ec

Authored by Alexandre A. Barbosa
2 parents 71357f92 4674eafd

Merge pull request #86 from colab/paginate-threads

Paginate threads
colab/accounts/utils/mailman.py
... ... @@ -164,4 +164,5 @@ def extract_listname_from_list(lists):
164 164 try:
165 165 return [mlist.get('listname') for mlist in lists]
166 166 except ValueError:
  167 + LOGGER.exception('listname not available')
167 168 return []
... ...
colab/super_archives/models.py
... ... @@ -88,6 +88,8 @@ class MailingList(models.Model):
88 88 last_imported_index = models.IntegerField(default=0)
89 89 is_private = models.BooleanField(default=False)
90 90  
  91 + _max_latest_threads = 6
  92 +
91 93 def update_privacy(self):
92 94 self.is_private = mailman.is_private_list(self.name)
93 95  
... ... @@ -100,6 +102,20 @@ class MailingList(models.Model):
100 102 return u'{}?{}'.format(reverse('haystack_search'),
101 103 urllib.urlencode(params))
102 104  
  105 + def get_latest(self):
  106 + not_spam_latest = self.thread_set.filter(spam=False)
  107 + ordered_latest = not_spam_latest.order_by(
  108 + '-latest_message__received_time')
  109 + return ordered_latest[:self._max_latest_threads]
  110 +
  111 + def get_most_relevant(self):
  112 + all_most_relevant = Thread.highest_score.filter(
  113 + mailinglist__name=self.name)[:self._max_latest_threads]
  114 + return [thread.latest_message for thread in all_most_relevant]
  115 +
  116 + def get_number_of_users(self):
  117 + return len(mailman.list_users(self.name))
  118 +
103 119 def __unicode__(self):
104 120 return self.name
105 121  
... ...
colab/super_archives/templates/superarchives/thread-dashboard.html
... ... @@ -7,11 +7,11 @@
7 7 <h2>{% trans 'Groups'|title %}</h2>
8 8 <hr/>
9 9  
10   - {% for listname, description, latest, most_relevant, number_of_users in lists %}
11   - {% if latest or most_relevant %}
12   - <h3><b>{{ listname|title|lower }} {% if description %} ({{ description }}){% endif %}</b></h3>
  10 + {% for mailinglist in lists %}
  11 + {% if mailinglist.get_latest or mailinglist.get_most_relevant %}
  12 + <h3><b>{{ mailinglist.name|title|lower }} {% if mailinglist.description %} ({{ mailinglist.description }}){% endif %}</b></h3>
13 13 <div class="btn-group btn-group-sm">
14   - <a href="#" class="btn btn-default" disabled="disabled">{% blocktrans %}{{ number_of_users }} members{% endblocktrans %}</a>
  14 + <a href="#" class="btn btn-default" disabled="disabled">{% blocktrans with number_of_users=mailinglist.get_number_of_users %}{{ number_of_users }} members{% endblocktrans %}</a>
15 15 </div>
16 16 <hr/>
17 17  
... ... @@ -19,12 +19,12 @@
19 19 <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
20 20 <h4>{% trans 'latest'|title %}</h4>
21 21 <ul class="message-list">
22   - {% for thread in latest %}
  22 + {% for thread in mailinglist.get_latest %}
23 23 {% include "message-preview.html" with result=thread.latest_message %}
24 24 {% endfor %}
25 25 </ul>
26 26 <div class="text-right">
27   - <a href="{% url 'haystack_search' %}?order=latest&list={{ listname }}&type=thread">
  27 + <a href="{% url 'haystack_search' %}?order=latest&list={{ mailinglist.name }}&type=thread">
28 28 {% trans "more..." %}
29 29 </a>
30 30 </div>
... ... @@ -33,12 +33,12 @@
33 33 <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
34 34 <h4>{% trans 'most relevant'|title %}</h4>
35 35 <ul class="message-list">
36   - {% for thread in most_relevant %}
  36 + {% for thread in mailinglist.get_most_relevant %}
37 37 {% include "message-preview.html" with result=thread %}
38 38 {% endfor %}
39 39 </ul>
40 40 <div class="text-right">
41   - <a href="{% url 'haystack_search' %}?list={{ listname }}&type=thread">
  41 + <a href="{% url 'haystack_search' %}?list={{ mailinglist.name }}&type=thread">
42 42 {% trans "more..." %}
43 43 </a>
44 44 </div>
... ... @@ -49,4 +49,52 @@
49 49 {% endif %}
50 50 {% endfor %}
51 51  
  52 + {% if page_obj.has_other_pages %}
  53 + <div class="text-center">
  54 + <ul class="pagination">
  55 + <li {% if page_obj.number == 1 %}class="disabled"{% endif %}>
  56 + <a href="{% if page_obj.number == 1 %}javascript:void(0);{% else %}?page={{1}}{% endif %}"><span class="glyphicon glyphicon-chevron-left small-icon"></span><span class="glyphicon glyphicon-chevron-left small-icon"></span></a>
  57 + </li>
  58 + <li {% if not page_obj.has_previous %}class="disabled"{% endif %}>
  59 + <a href="{% if page_obj.has_previous %}?page={{page_obj.previous_page_number }}{% else %}javascript:void(0);{% endif %}"><span class="glyphicon glyphicon-chevron-left small-icon"></span></a>
  60 + </li>
  61 +
  62 + {% if page_obj.has_previous %}
  63 + {% if page_obj.previous_page_number > 1 %}
  64 + <li>
  65 + <a href="?page={{page_obj.previous_page_number|add:-2 }}">{{ page_obj.number|add:-2 }}</a>
  66 + </li>
  67 + {% endif %}
  68 + <li>
  69 + <a href="?page={{page_obj.previous_page_number }}">{{ page_obj.number|add:-1 }}</a>
  70 + </li>
  71 + {% endif %}
  72 +
  73 + <li class="active">
  74 + <a href="javascript:void(0);">{{ page_obj.number }}</a>
  75 + </li>
  76 +
  77 + {% if page_obj.has_next %}
  78 + <li>
  79 + <a href="?page={{page_obj.next_page_number }}">{{ page_obj.number|add:1 }}</a>
  80 + </li>
  81 + {% if page_obj.next_page_number < page_obj.paginator.num_pages %}
  82 + <li>
  83 + <a href="?page={{page_obj.next_page_number|add:1 }}">{{ page_obj.number|add:2 }}</a>
  84 + </li>
  85 + {% endif %}
  86 + {% endif %}
  87 +
  88 + <li {% if not page_obj.has_next %}class="disabled"{% endif %}>
  89 + <a href="{% if page_obj.has_next %}?page={{page_obj.next_page_number }}{% else %}javascript:void(0);{% endif %}"><span class="glyphicon glyphicon-chevron-right small-icon"></span></a>
  90 + </li>
  91 +
  92 + <li {% if page_obj.number == page_obj.paginator.num_pages %}class="disabled"{% endif %}>
  93 + <a href="{% if page_obj.number == page_obj.paginator.num_pages %}javascript:void(0);{% else %}?page={{page_obj.paginator.num_pages }}{% endif %}"><span class="glyphicon glyphicon-chevron-right small-icon"></span><span class="glyphicon glyphicon-chevron-right small-icon"></span></a>
  94 + </li>
  95 +
  96 + </ul>
  97 + </div>
  98 + {% endif %}
  99 +
52 100 {% endblock %}
... ...
colab/super_archives/tests/test_privatelist.py
... ... @@ -17,18 +17,18 @@ class ArchivesViewTest(TestCase):
17 17  
18 18 def test_see_only_private_list_if_member(self):
19 19 mailman.get_user_mailinglists = mock.Mock(
20   - return_value="[{'listname': 'privatelist'}]")
  20 + return_value=[{'listname': 'privatelist'}])
21 21 mailman.extract_listname_from_list = mock.Mock(
22   - return_value="['privatelist']")
23   - mailman.list_users = mock.Mock(return_value="['johndoe@example.com']")
  22 + return_value=['privatelist'])
  23 + mailman.list_users = mock.Mock(return_value=['johndoe@example.com'])
24 24  
25 25 self.authenticate_user()
26 26 request = self.client.get('/archives/thread/')
27 27  
28 28 list_data = request.context['lists']
29 29  
30   - self.assertEqual('lista', list_data[0][0])
31   - self.assertEqual('privatelist', list_data[1][0])
  30 + self.assertEqual('lista', list_data[0].name)
  31 + self.assertEqual('privatelist', list_data[1].name)
32 32 self.assertEqual(2, len(list_data))
33 33  
34 34 def test_see_only_public_if_not_logged_in(self):
... ... @@ -36,7 +36,7 @@ class ArchivesViewTest(TestCase):
36 36  
37 37 list_data = request.context['lists']
38 38  
39   - self.assertEqual('lista', list_data[0][0])
  39 + self.assertEqual('lista', list_data[0].name)
40 40 self.assertEqual(1, len(list_data))
41 41  
42 42 def test_see_private_thread_in_dashboard_if_member(self):
... ...
colab/super_archives/views.py
... ... @@ -11,7 +11,8 @@ from django.conf import settings
11 11 from django.contrib import messages
12 12 from django.core.urlresolvers import reverse
13 13 from django.db import IntegrityError
14   -from django.views.generic import View
  14 +from django.db.models import Q
  15 +from django.views.generic import View, ListView
15 16 from django.utils.translation import ugettext as _
16 17 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
17 18 from django.utils.decorators import method_decorator
... ... @@ -137,42 +138,23 @@ class ThreadView(View):
137 138 return self.get(request, mailinglist, thread_token)
138 139  
139 140  
140   -class ThreadDashboardView(View):
  141 +class ThreadDashboardView(ListView):
141 142 http_method_names = ['get']
  143 + context_object_name = 'lists'
  144 + template_name = 'superarchives/thread-dashboard.html'
  145 + paginate_by = 10
142 146  
143   - def get(self, request):
144   - MAX = 6
145   - context = {}
146   -
147   - all_privates = {}
148   - private_mailinglist = MailingList.objects.filter(is_private=True)
149   - for mailinglist in private_mailinglist:
150   - all_privates[mailinglist.name] = True
151   -
152   - context['lists'] = []
153   -
  147 + def get_queryset(self):
154 148 listnames_for_user = []
155   - if request.user.is_authenticated():
156   - user = User.objects.get(username=request.user)
  149 + if self.request.user.is_authenticated():
  150 + user = User.objects.get(username=self.request.user)
157 151 lists_for_user = mailman.get_user_mailinglists(user)
158 152 listnames_for_user = mailman.extract_listname_from_list(
159 153 lists_for_user)
160 154  
161   - for list_ in MailingList.objects.order_by('name'):
162   - if list_.name not in all_privates\
163   - or list_.name in listnames_for_user:
164   - context['lists'].append((
165   - list_.name,
166   - mailman.get_list_description(list_.name),
167   - list_.thread_set.filter(spam=False).order_by(
168   - '-latest_message__received_time'
169   - )[:MAX],
170   - [t.latest_message for t in Thread.highest_score.filter(
171   - mailinglist__name=list_.name)[:MAX]],
172   - len(mailman.list_users(list_.name)),
173   - ))
174   -
175   - return render(request, 'superarchives/thread-dashboard.html', context)
  155 + query = Q(is_private=False) | Q(name__in=listnames_for_user)
  156 +
  157 + return MailingList.objects.filter(query).order_by('name')
176 158  
177 159  
178 160 class EmailView(View):
... ...