Commit 5f62a7ac63ea3282a137553bc5800a93691f04d0

Authored by Sergio Oliveira
2 parents 25aa2f67 d601d716

Merge branch 'master' of beta.softwarepublico.gov.br:softwarepublico/colab

colab/accounts/views.py
1   -#!/usr/bin/env python
2 1 # encoding: utf-8
3   -
4 2 from collections import OrderedDict
5 3  
6   -from haystack.exceptions import SearchBackendError
7   -
8 4 from django.conf import settings
9 5 from django.contrib import messages
10 6 from django.db import transaction
... ... @@ -18,15 +14,13 @@ from django.views.generic import DetailView, UpdateView, TemplateView
18 14  
19 15 from conversejs import xmpp
20 16 from conversejs.models import XMPPAccount
21   -from haystack.query import SearchQuerySet
22 17  
23 18 from colab.super_archives.models import (EmailAddress, Message,
24 19 EmailAddressValidation)
25   -from colab.search.utils import trans
26   -# from proxy.trac.models import WikiCollabCount, TicketCollabCount
  20 +from colab.search.utils import get_collaboration_data
  21 +
27 22 from .forms import (UserCreationForm, UserForm, ListsForm,
28 23 UserUpdateForm, ChangeXMPPPasswordForm)
29   -# from .errors import XMPPChangePwdException
30 24 from .utils import mailman
31 25  
32 26  
... ... @@ -71,54 +65,21 @@ class UserProfileDetailView(UserProfileBaseMixin, DetailView):
71 65  
72 66 count_types = OrderedDict()
73 67  
74   - fields_or_lookup = (
75   - {'collaborators__contains': user.username},
76   - {'fullname_and_username__contains': user.username},
77   - )
78   -
79   - counter_class = {}
80   - # {
81   - # 'wiki': WikiCollabCount,
82   - # 'ticket': TicketCollabCount,
83   - # }
  68 + collaborations, count_types_extras = get_collaboration_data(user)
84 69  
85   - types = ['thread']
86   - # types.extend(['ticket', 'wiki', 'changeset', 'attachment'])
  70 + collaborations.sort(key=lambda elem: elem.modified, reverse=True)
87 71  
88   - messages = Message.objects.filter(from_address__user__pk=user.pk)
89   - for type in types:
90   - CounterClass = counter_class.get(type)
91   - if CounterClass:
92   - try:
93   - counter = CounterClass.objects.get(author=user.username)
94   - except CounterClass.DoesNotExist:
95   - count_types[trans(type)] = 0
96   - else:
97   - count_types[trans(type)] = counter.count
98   - elif type == 'thread':
99   - count_types[trans(type)] = messages.count()
100   - else:
101   - sqs = SearchQuerySet()
102   - for filter_or in fields_or_lookup:
103   - sqs = sqs.filter_or(type=type, **filter_or)
104   - count_types[trans(type)] = sqs.count()
  72 + count_types.update(count_types_extras)
105 73  
106 74 context['type_count'] = count_types
107   -
108   - sqs = SearchQuerySet()
109   - for filter_or in fields_or_lookup:
110   - sqs = sqs.filter_or(**filter_or).exclude(type='thread')
111   -
112   - try:
113   - context['results'] = sqs.order_by('-modified', '-created')[:10]
114   - except SearchBackendError:
115   - context['results'] = sqs.order_by('-modified')[:10]
  75 + context['results'] = collaborations[:10]
116 76  
117 77 email_pks = [addr.pk for addr in user.emails.iterator()]
118 78 query = Message.objects.filter(from_address__in=email_pks)
119 79 query = query.order_by('-received_time')
120 80 context['emails'] = query[:10]
121 81  
  82 + messages = Message.objects.filter(from_address__user__pk=user.pk)
122 83 count_by = 'thread__mailinglist__name'
123 84 context['list_activity'] = dict(messages.values_list(count_by)
124 85 .annotate(Count(count_by))
... ...
colab/home/views.py
1   -
2   -from collections import OrderedDict
3   -
4 1 from django.conf import settings
5   -from django.core.cache import cache
6 2 from django.shortcuts import render
7 3 from django.http import HttpResponse, Http404
8 4  
9   -from haystack.query import SearchQuerySet
10   -
11   -# from proxy.trac.models import WikiCollabCount, TicketCollabCount
12   -from colab.search.utils import trans
  5 +from colab.search.utils import get_collaboration_data
13 6 from colab.super_archives.models import Thread
14 7  
15 8  
16 9 def dashboard(request):
17 10 """Dashboard page"""
  11 +
  12 + highest_score_threads = Thread.highest_score.all()[:6]
  13 +
  14 + hottest_threads = [t.latest_message for t in highest_score_threads]
  15 +
18 16 latest_threads = Thread.objects.all()[:6]
19   - hottest_threads = Thread.highest_score.from_haystack()[:6]
20   -
21   - count_types = cache.get('home_chart')
22   - if count_types is None:
23   - count_types = OrderedDict()
24   - count_types['thread'] = SearchQuerySet().filter(
25   - type='thread',
26   - ).count()
27   - # TODO: this section should be inside trac app and only use it here
28   - # if settings.TRAC_ENABLED:
29   - # for type in ['changeset', 'attachment']:
30   - # count_types[type] = SearchQuerySet().filter(
31   - # type=type,
32   - # ).count()
33   -
34   - # count_types['ticket'] = sum([
35   - # ticket.count for ticket in TicketCollabCount.objects.all()
36   - # ])
37   -
38   - # count_types['wiki'] = sum([
39   - # wiki.count for wiki in WikiCollabCount.objects.all()
40   - # ])
41   -
42   - cache.set('home_chart', count_types)
43   -
44   - for key in count_types.keys():
45   - count_types[trans(key)] = count_types.pop(key)
  17 +
  18 + latest_results, count_types = get_collaboration_data()
  19 + latest_results.sort(key=lambda elem: elem.modified, reverse=True)
46 20  
47 21 context = {
48 22 'hottest_threads': hottest_threads[:6],
49 23 'latest_threads': latest_threads,
50 24 'type_count': count_types,
51   - 'latest_results': SearchQuerySet().all().order_by(
52   - '-modified', '-created'
53   - )[:6],
  25 + 'latest_results': latest_results[:6],
54 26 }
55 27 return render(request, 'home.html', context)
56 28  
... ...
colab/proxy/gitlab/data_api.py
1   -
2 1 import json
3 2 import urllib
4 3 import urllib2
... ... @@ -58,7 +57,7 @@ class GitlabDataAPI(ProxyDataAPI):
58 57  
59 58 return projects
60 59  
61   - def fetchData(self):
  60 + def fetch_data(self):
62 61 data = self.fetchProjects()
63 62  
64 63 for datum in data:
... ...
colab/proxy/gitlab/models.py
1 1 from django.db import models
  2 +from django.utils.translation import ugettext_lazy as _
2 3  
3 4  
4 5 class GitlabProject(models.Model):
... ... @@ -10,3 +11,7 @@ class GitlabProject(models.Model):
10 11 name_with_namespace = models.TextField()
11 12 created_at = models.DateTimeField(blank=True)
12 13 last_activity_at = models.DateTimeField(blank=True)
  14 +
  15 + class Meta:
  16 + verbose_name = _('Gitlab Project')
  17 + verbose_name_plural = _('Gitlab Projects')
... ...
colab/proxy/jenkins/data_api.py
... ... @@ -3,5 +3,5 @@ from colab.proxy.utils.proxy_data_api import ProxyDataAPI
3 3  
4 4 class JenkinsDataAPI(ProxyDataAPI):
5 5  
6   - def fetchData(self):
  6 + def fetch_data(self):
7 7 pass
... ...
colab/proxy/management/commands/import_proxy_data.py
1 1 #!/usr/bin/env python
2 2  
3 3 import importlib
  4 +import inspect
4 5  
5 6 from django.core.management.base import BaseCommand
6 7 from django.conf import settings
7 8  
8   -from colab.proxy.proxybase.proxy_data_api import ProxyDataAPI
  9 +from colab.proxy.utils.proxy_data_api import ProxyDataAPI
9 10  
10 11  
11 12 class Command(BaseCommand):
... ... @@ -20,7 +21,10 @@ class Command(BaseCommand):
20 21  
21 22 for module_item_name in dir(module):
22 23 module_item = getattr(module, module_item_name)
  24 + if not inspect.isclass(module_item):
  25 + continue
23 26 if issubclass(module_item, ProxyDataAPI):
24 27 if module_item != ProxyDataAPI:
25 28 api = module_item()
26   - api.fetchData()
  29 + api.fetch_data()
  30 + break
... ...
colab/proxy/noosfero/data_api.py
... ... @@ -3,5 +3,5 @@ from colab.proxy.utils.proxy_data_api import ProxyDataAPI
3 3  
4 4 class NoosferoDataAPI(ProxyDataAPI):
5 5  
6   - def fetchData(self):
  6 + def fetch_data(self):
7 7 pass
... ...
colab/proxy/redmine/data_api.py
... ... @@ -3,5 +3,5 @@ from colab.proxy.utils.proxy_data_api import ProxyDataAPI
3 3  
4 4 class RedmineDataAPI(ProxyDataAPI):
5 5  
6   - def fetchData(self):
  6 + def fetch_data(self):
7 7 pass
... ...
colab/proxy/trac/data_api.py
... ... @@ -3,5 +3,5 @@ from colab.proxy.utils.proxy_data_api import ProxyDataAPI
3 3  
4 4 class TracDataAPI(ProxyDataAPI):
5 5  
6   - def fetchData(self):
  6 + def fetch_data(self):
7 7 pass
... ...
colab/proxy/trac/models.py
... ... @@ -131,21 +131,3 @@ class Wiki(models.Model, HitCounterModelMixin):
131 131 return User.objects.get(username=self.modified_by)
132 132 except User.DoesNotExist:
133 133 return None
134   -
135   -
136   -class WikiCollabCount(models.Model):
137   - author = models.TextField(primary_key=True)
138   - count = models.IntegerField()
139   -
140   - class Meta:
141   - managed = False
142   - db_table = 'wiki_collab_count_view'
143   -
144   -
145   -class TicketCollabCount(models.Model):
146   - author = models.TextField(primary_key=True)
147   - count = models.IntegerField()
148   -
149   - class Meta:
150   - managed = False
151   - db_table = 'ticket_collab_count_view'
... ...
colab/proxy/utils/models.py 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +from django.db import models
  2 +from django.conf import settings
  3 +from colab.accounts.models import User
  4 +
  5 +
  6 +class Collaboration(models.Model):
  7 + '''
  8 + Class to define the fields of the collaboration block
  9 + that are displayed at dashboard and profile pages.
  10 + '''
  11 +
  12 + tag = None
  13 + title = None
  14 + description = None
  15 + url = None
  16 + modified = None
  17 + type = None
  18 +
  19 + user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
  20 + on_delete=models.SET_NULL)
  21 +
  22 + @property
  23 + def modified_by(self):
  24 + if self.user:
  25 + return self.user.get_full_name()
  26 + return None
  27 +
  28 + @property
  29 + def modified_by_url(self):
  30 + if self.user:
  31 + return self.user.get_absolute_url()
  32 + return None
  33 +
  34 + def update_user(self, user_name):
  35 + try:
  36 + self.user = User.objects.get(username=user_name)
  37 + except User.DoesNotExist:
  38 + self.user = None
  39 +
  40 + class Meta:
  41 + abstract = True
... ...
colab/proxy/utils/proxy_data_api.py
... ... @@ -2,5 +2,5 @@
2 2  
3 3 class ProxyDataAPI(object):
4 4  
5   - def fetchData(self):
  5 + def fetch_data(self):
6 6 raise NotImplementedError('fetchData not yet implemented')
... ...
colab/search/templates/search/search-message-preview.html
... ... @@ -11,7 +11,6 @@
11 11 <span class="subject">
12 12 <a href="{{ result.url }}#msg-{{ result.pk }}"
13 13 title="{% filter striptags|truncatewords:50 %}{{ result.description|escape }}{% endfilter %}">
14   - {{ result.title }}
15 14 </a>
16 15 </span>
17 16  
... ...
colab/search/utils.py
  1 +import importlib
  2 +import inspect
1 3  
  4 +from collections import OrderedDict
  5 +
  6 +from django.core.cache import cache
2 7 from django.utils.translation import ugettext as _
  8 +from django.conf import settings
  9 +from colab.super_archives.models import Thread, Message
  10 +from colab.proxy.utils.models import Collaboration
  11 +
  12 +
  13 +def get_collaboration_data(filter_by_user=None):
  14 + latest_results = []
  15 + count_types = cache.get('home_chart')
  16 + populate_count_types = False
  17 +
  18 + if count_types is None:
  19 + populate_count_types = True
  20 + count_types = OrderedDict()
  21 + count_types[_('Emails')] = Thread.objects.count()
  22 +
  23 + if filter_by_user:
  24 + messages = Message.objects.filter(from_address__user__pk=filter_by_user.pk)
  25 + else:
  26 + latest_threads = Thread.objects.all()[:6]
  27 + messages = [t.latest_message for t in latest_threads]
  28 +
  29 + latest_results.extend(messages)
  30 +
  31 + app_names = settings.PROXIED_APPS.keys()
  32 +
  33 + for app_name in app_names:
  34 + module = importlib \
  35 + .import_module('colab.proxy.{}.models'.format(app_name))
  36 +
  37 + for module_item_name in dir(module):
  38 + module_item = getattr(module, module_item_name)
  39 + if not inspect.isclass(module_item):
  40 + continue
  41 + if not issubclass(module_item, Collaboration):
  42 + continue
  43 + if module_item == Collaboration:
  44 + continue
  45 +
  46 + queryset = module_item.objects
  47 +
  48 + if filter_by_user:
  49 + elements = queryset.filter(
  50 + user__username=filter_by_user)
  51 + else:
  52 + elements = queryset.all()
  53 +
  54 + latest_results.extend(elements)
  55 + elements_count = elements.count()
  56 +
  57 + if elements_count > 1:
  58 + verbose_name = module_item._meta.verbose_name_plural.title()
  59 + else:
  60 + verbose_name = module_item._meta.verbose_name.title()
3 61  
  62 + if populate_count_types:
  63 + count_types[verbose_name] = elements_count
4 64  
5   -def trans(key):
6   - translations = {
7   - 'wiki': _('Wiki'),
8   - 'thread': _('Emails'),
9   - 'changeset': _('Code'),
10   - 'ticket': _('Tickets'),
11   - 'attachment': _('Attachments'),
12   - }
  65 + if populate_count_types:
  66 + cache.set('home_chart', count_types, 30)
13 67  
14   - return translations.get(key, key)
  68 + return latest_results, count_types
... ...
colab/settings.py
... ... @@ -321,8 +321,9 @@ if FEEDZILLA_ENABLED:
321 321 'common',
322 322 )
323 323  
324   -PROXIED_APPS = locals().get('PROXIED_APPS') or {}
325 324 BROWSERID_ENABLED = locals().get('BROWSERID_ENABLED') or False
326 325  
  326 +PROXIED_APPS = locals().get('PROXIED_APPS') or {}
  327 +
327 328 for app_label in PROXIED_APPS.keys():
328 329 INSTALLED_APPS += ('colab.proxy.{}'.format(app_label),)
... ...
colab/super_archives/views.py
... ... @@ -17,13 +17,11 @@ from django.utils.decorators import method_decorator
17 17 from django.contrib.auth.decorators import login_required
18 18 from django.shortcuts import render, redirect, get_object_or_404
19 19  
20   -from haystack.query import SearchQuerySet
21   -
22 20 from colab.accounts.utils import mailman
23 21 from colab.accounts.models import User
24 22 from .utils.email import send_verification_email
25   -from .models import MailingList, Thread, EmailAddress
26   -from .models import EmailAddressValidation, Message
  23 +from .models import (MailingList, Thread, EmailAddress,
  24 + EmailAddressValidation, Message)
27 25  
28 26  
29 27 class ThreadView(View):
... ... @@ -110,8 +108,9 @@ class ThreadView(View):
110 108 elif resp.status_code == 404:
111 109 error_msg = _('Mailing list does not exist')
112 110 else:
113   - error_msg = _('Unknown error\
114   - trying to connect to Mailman API')
  111 + error_msg = \
  112 + _('Unknown error trying to connect to Mailman API')
  113 +
115 114 messages.error(request, error_msg)
116 115  
117 116 return self.get(request, mailinglist, thread_token)
... ... @@ -126,7 +125,7 @@ class ThreadDashboardView(View):
126 125 all_lists = mailman.all_lists(description=True)
127 126  
128 127 context['lists'] = []
129   - # lists = MailingList.objects.filter()
  128 +
130 129 for list_ in MailingList.objects.order_by('name'):
131 130 context['lists'].append((
132 131 list_.name,
... ... @@ -134,7 +133,8 @@ class ThreadDashboardView(View):
134 133 list_.thread_set.filter(spam=False).order_by(
135 134 '-latest_message__received_time'
136 135 )[:MAX],
137   - SearchQuerySet().filter(type='thread', tag=list_.name)[:MAX],
  136 + [t.latest_message for t in Thread.highest_score.filter(
  137 + mailinglist__name=list_.name)[:MAX]],
138 138 len(mailman.list_users(list_.name)),
139 139 ))
140 140  
... ... @@ -171,6 +171,7 @@ class EmailView(View):
171 171 email.user = email_val.user
172 172 email.save()
173 173 email_val.delete()
  174 +
174 175 user = User.objects.get(username=email.user.username)
175 176 user.is_active = True
176 177 user.save()
... ...
tests/test_data.json 0 → 100644
... ... @@ -0,0 +1,149 @@
  1 +[
  2 +{
  3 + "fields": {
  4 + "logo": "",
  5 + "description": "",
  6 + "last_imported_index": 0,
  7 + "name": "ListA",
  8 + "email": "listA@example.com"
  9 + },
  10 + "model": "super_archives.mailinglist",
  11 + "pk": 1
  12 +},
  13 +{
  14 + "fields": {
  15 + "logo": "",
  16 + "description": "",
  17 + "last_imported_index": 0,
  18 + "name": "ListB",
  19 + "email": "listB@example.com"
  20 + },
  21 + "model": "super_archives.mailinglist",
  22 + "pk": 2
  23 +},
  24 +{
  25 + "fields": {
  26 + "logo": "",
  27 + "description": "",
  28 + "last_imported_index": 0,
  29 + "name": "ListC",
  30 + "email": "listC@example.com"
  31 + },
  32 + "model": "super_archives.mailinglist",
  33 + "pk": 3
  34 +},
  35 +{
  36 + "fields": {
  37 + "spam": false,
  38 + "subject_token": "Thread_1_on_List_A",
  39 + "mailinglist": 1,
  40 + "score": 31,
  41 + "latest_message": 3
  42 + },
  43 + "model": "super_archives.thread",
  44 + "pk": 1
  45 +},
  46 +{
  47 + "fields": {
  48 + "spam": false,
  49 + "subject_token": "Thread_1_on_List_B",
  50 + "mailinglist": 2,
  51 + "score": 0,
  52 + "latest_message": 4
  53 + },
  54 + "model": "super_archives.thread",
  55 + "pk": 3
  56 +},
  57 +{
  58 + "fields": {
  59 + "spam": false,
  60 + "subject_token": "Thread_1_on_List_C",
  61 + "mailinglist": 3,
  62 + "score": 0,
  63 + "latest_message": 5
  64 + },
  65 + "model": "super_archives.thread",
  66 + "pk": 4
  67 +},
  68 +{
  69 + "fields": {
  70 + "body": "This is a repply to Thread 1 on list A",
  71 + "received_time": "2015-01-28T12:43:00.752Z",
  72 + "from_address": 1,
  73 + "thread": 1,
  74 + "spam": false,
  75 + "subject_clean": "Response to Thread 1A",
  76 + "message_id": "loreipsum",
  77 + "subject": "Response to Thread 1A"
  78 + },
  79 + "model": "super_archives.message",
  80 + "pk": 3
  81 +},
  82 +{
  83 + "fields": {
  84 + "body": "",
  85 + "received_time": "2015-01-28T12:57:22.180Z",
  86 + "from_address": 1,
  87 + "thread": 3,
  88 + "spam": false,
  89 + "subject_clean": "Message 1 on Thread 1B",
  90 + "message_id": "",
  91 + "subject": "Message 1 on Thread 1B"
  92 + },
  93 + "model": "super_archives.message",
  94 + "pk": 4
  95 +},
  96 +{
  97 + "fields": {
  98 + "body": "",
  99 + "received_time": "2015-01-28T13:02:12.903Z",
  100 + "from_address": 1,
  101 + "thread": 4,
  102 + "spam": false,
  103 + "subject_clean": "Message 1 on Thread 1C",
  104 + "message_id": "",
  105 + "subject": "Message 1 on Thread 1C"
  106 + },
  107 + "model": "super_archives.message",
  108 + "pk": 5
  109 +},
  110 +{
  111 + "fields": {
  112 + "last_name": "Administrator",
  113 + "webpage": "",
  114 + "twitter": "",
  115 + "is_staff": true,
  116 + "user_permissions": [],
  117 + "date_joined": "2015-01-28T12:34:58.770Z",
  118 + "google_talk": "",
  119 + "first_name": "Admin",
  120 + "is_superuser": true,
  121 + "last_login": "2015-01-28T12:35:39.621Z",
  122 + "verification_hash": null,
  123 + "role": "",
  124 + "email": "admin@mail.com",
  125 + "username": "admin",
  126 + "bio": "",
  127 + "needs_update": true,
  128 + "is_active": true,
  129 + "facebook": "",
  130 + "groups": [],
  131 + "password": "pbkdf2_sha256$12000$iiKCMnLZnFJw$UTx89LB8oYTiw9UqkcglzFLmIaZtbr+ZzF1cG3vfcyo=",
  132 + "institution": "",
  133 + "github": "",
  134 + "modified": "2015-01-28T12:45:27.375Z"
  135 + },
  136 + "model": "accounts.user",
  137 + "pk": 1
  138 +},
  139 +{
  140 + "fields": {
  141 + "real_name": "",
  142 + "user": 1,
  143 + "md5": "edb0e96701c209ab4b50211c856c50c4",
  144 + "address": "admin@mail.com"
  145 + },
  146 + "model": "super_archives.emailaddress",
  147 + "pk": 1
  148 +}
  149 +]
... ...
vagrant/provision.sh
... ... @@ -31,3 +31,4 @@ if [ ! -s /etc/colab/settings.yaml ]; then
31 31 fi
32 32  
33 33 colab-admin migrate
  34 +colab-admin loaddata /vagrant/tests/test_data.json
... ...