Commit ee69bd3a7f847b2f882d36b88d2a467e4a97116e
Exists in
master
and in
39 other branches
Merge branch 'master' of github.com:TracyWebTech/colab
Showing
17 changed files
with
695 additions
and
104 deletions
Show diff stats
TODO.rst
| @@ -47,6 +47,7 @@ Interface | @@ -47,6 +47,7 @@ Interface | ||
| 47 | * Paginar discussoes | 47 | * Paginar discussoes |
| 48 | * Paginar dashboard de discussoes | 48 | * Paginar dashboard de discussoes |
| 49 | * Highlight mostrar resultados normalizados (com e sem acentos) | 49 | * Highlight mostrar resultados normalizados (com e sem acentos) |
| 50 | +* Adicionar opção para escolha de idioma | ||
| 50 | 51 | ||
| 51 | 52 | ||
| 52 | Outros | 53 | Outros |
requirements.txt
src/accounts/migrations/0006_auto__add_field_user_modified.py
0 → 100644
| @@ -0,0 +1,69 @@ | @@ -0,0 +1,69 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | +from south.db import db | ||
| 3 | +from south.v2 import SchemaMigration | ||
| 4 | +from django.db import models | ||
| 5 | +from django.utils import timezone | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class Migration(SchemaMigration): | ||
| 9 | + | ||
| 10 | + def forwards(self, orm): | ||
| 11 | + # Adding field 'User.modified' | ||
| 12 | + db.add_column(u'accounts_user', 'modified', | ||
| 13 | + self.gf('django.db.models.fields.DateTimeField')(auto_now=True, default=timezone.now, blank=True), | ||
| 14 | + keep_default=False) | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + def backwards(self, orm): | ||
| 18 | + # Deleting field 'User.modified' | ||
| 19 | + db.delete_column(u'accounts_user', 'modified') | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + models = { | ||
| 23 | + u'accounts.user': { | ||
| 24 | + 'Meta': {'object_name': 'User'}, | ||
| 25 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 26 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | ||
| 27 | + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 28 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 29 | + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), | ||
| 30 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | ||
| 31 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 32 | + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 33 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
| 34 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 35 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 36 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 37 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 38 | + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), | ||
| 39 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
| 40 | + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 41 | + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 42 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | ||
| 43 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), | ||
| 44 | + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), | ||
| 45 | + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) | ||
| 46 | + }, | ||
| 47 | + u'auth.group': { | ||
| 48 | + 'Meta': {'object_name': 'Group'}, | ||
| 49 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 50 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
| 51 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
| 52 | + }, | ||
| 53 | + u'auth.permission': { | ||
| 54 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | ||
| 55 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 56 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
| 57 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 58 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
| 59 | + }, | ||
| 60 | + u'contenttypes.contenttype': { | ||
| 61 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
| 62 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 63 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 64 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 65 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + complete_apps = ['accounts'] |
src/accounts/models.py
| @@ -16,6 +16,7 @@ class User(AbstractUser): | @@ -16,6 +16,7 @@ class User(AbstractUser): | ||
| 16 | google_talk = models.EmailField(null=True, blank=True) | 16 | google_talk = models.EmailField(null=True, blank=True) |
| 17 | webpage = models.CharField(max_length=256, null=True, blank=True) | 17 | webpage = models.CharField(max_length=256, null=True, blank=True) |
| 18 | verification_hash = models.CharField(max_length=32, null=True, blank=True) | 18 | verification_hash = models.CharField(max_length=32, null=True, blank=True) |
| 19 | + modified = models.DateTimeField(auto_now=True) | ||
| 19 | 20 | ||
| 20 | def get_absolute_url(self): | 21 | def get_absolute_url(self): |
| 21 | return reverse('user_profile', kwargs={'username': self.username}) | 22 | return reverse('user_profile', kwargs={'username': self.username}) |
src/accounts/search_indexes.py
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
| 2 | 2 | ||
| 3 | from haystack import indexes | 3 | from haystack import indexes |
| 4 | +from django.db.models import Count | ||
| 4 | 5 | ||
| 6 | +from proxy.models import Revision, Ticket, Wiki | ||
| 7 | +from badger.utils import get_users_counters | ||
| 5 | from .models import User | 8 | from .models import User |
| 6 | 9 | ||
| 7 | 10 | ||
| 8 | class UserIndex(indexes.SearchIndex, indexes.Indexable): | 11 | class UserIndex(indexes.SearchIndex, indexes.Indexable): |
| 9 | # common fields | 12 | # common fields |
| 10 | - text = indexes.CharField(document=True, use_template=True) | ||
| 11 | - url = indexes.CharField(model_attr='get_absolute_url') | 13 | + text = indexes.CharField(document=True, use_template=True, stored=False) |
| 14 | + url = indexes.CharField(model_attr='get_absolute_url', indexed=False) | ||
| 12 | title = indexes.CharField(model_attr='get_full_name') | 15 | title = indexes.CharField(model_attr='get_full_name') |
| 13 | description = indexes.CharField(null=True) | 16 | description = indexes.CharField(null=True) |
| 14 | type = indexes.CharField() | 17 | type = indexes.CharField() |
| 15 | icon_name = indexes.CharField() | 18 | icon_name = indexes.CharField() |
| 16 | 19 | ||
| 17 | # extra fields | 20 | # extra fields |
| 18 | - username = indexes.CharField(model_attr='username') | 21 | + username = indexes.CharField(model_attr='username', stored=False) |
| 19 | name = indexes.CharField(model_attr='get_full_name') | 22 | name = indexes.CharField(model_attr='get_full_name') |
| 20 | - email = indexes.CharField(model_attr='email') | 23 | + email = indexes.CharField(model_attr='email', stored=False) |
| 21 | institution = indexes.CharField(model_attr='institution', null=True) | 24 | institution = indexes.CharField(model_attr='institution', null=True) |
| 22 | role = indexes.CharField(model_attr='role', null=True) | 25 | role = indexes.CharField(model_attr='role', null=True) |
| 23 | - google_talk = indexes.CharField(model_attr='google_talk', null=True) | ||
| 24 | - webpage = indexes.CharField(model_attr='webpage', null=True) | 26 | + google_talk = indexes.CharField(model_attr='google_talk', null=True, |
| 27 | + stored=False) | ||
| 28 | + webpage = indexes.CharField(model_attr='webpage', null=True, stored=False) | ||
| 29 | + message_count = indexes.IntegerField(stored=False) | ||
| 30 | + changeset_count = indexes.IntegerField(stored=False) | ||
| 31 | + ticket_count = indexes.IntegerField(stored=False) | ||
| 32 | + wiki_count = indexes.IntegerField(stored=False) | ||
| 33 | + contribution_count = indexes.IntegerField(stored=False) | ||
| 25 | 34 | ||
| 26 | def get_model(self): | 35 | def get_model(self): |
| 27 | return User | 36 | return User |
| 28 | 37 | ||
| 38 | + @property | ||
| 39 | + def badge_counters(self): | ||
| 40 | + if not hasattr(self, '_badge_counters'): | ||
| 41 | + self._badge_counters = get_users_counters() | ||
| 42 | + return self._badge_counters | ||
| 43 | + | ||
| 29 | def get_updated_field(self): | 44 | def get_updated_field(self): |
| 30 | - return 'date_joined' | 45 | + return 'modified' |
| 46 | + | ||
| 47 | + def prepare(self, obj): | ||
| 48 | + prepared_data = super(UserIndex, self).prepare(obj) | ||
| 49 | + | ||
| 50 | + prepared_data['contribution_count'] = sum(( | ||
| 51 | + self.prepared_data['message_count'], | ||
| 52 | + self.prepared_data['changeset_count'], | ||
| 53 | + self.prepared_data['ticket_count'], | ||
| 54 | + self.prepared_data['wiki_count'] | ||
| 55 | + )) | ||
| 56 | + | ||
| 57 | + return prepared_data | ||
| 31 | 58 | ||
| 32 | def prepare_description(self, obj): | 59 | def prepare_description(self, obj): |
| 33 | return u'{}\n{}\n{}\n{}'.format( | 60 | return u'{}\n{}\n{}\n{}'.format( |
| @@ -40,5 +67,17 @@ class UserIndex(indexes.SearchIndex, indexes.Indexable): | @@ -40,5 +67,17 @@ class UserIndex(indexes.SearchIndex, indexes.Indexable): | ||
| 40 | def prepare_type(self, obj): | 67 | def prepare_type(self, obj): |
| 41 | return u'user' | 68 | return u'user' |
| 42 | 69 | ||
| 70 | + def prepare_message_count(self, obj): | ||
| 71 | + return self.badge_counters[obj.username]['messages'] | ||
| 72 | + | ||
| 73 | + def prepare_changeset_count(self, obj): | ||
| 74 | + return self.badge_counters[obj.username]['revisions'] | ||
| 75 | + | ||
| 76 | + def prepare_ticket_count(self, obj): | ||
| 77 | + return self.badge_counters[obj.username]['tickets'] | ||
| 78 | + | ||
| 79 | + def prepare_wiki_count(self, obj): | ||
| 80 | + return self.badge_counters[obj.username]['wikis'] | ||
| 81 | + | ||
| 43 | def index_queryset(self, using=None): | 82 | def index_queryset(self, using=None): |
| 44 | return self.get_model().objects.filter(is_active=True) | 83 | return self.get_model().objects.filter(is_active=True) |
src/accounts/templates/accounts/user_detail.html
| 1 | {% extends "base.html" %} | 1 | {% extends "base.html" %} |
| 2 | -{% load i18n gravatar %} | 2 | + |
| 3 | +{% load i18n gravatar i18n_model %} | ||
| 4 | + | ||
| 3 | 5 | ||
| 4 | {% block head_js %} | 6 | {% block head_js %} |
| 5 | {% include "pizza-chart.html" with chart_data=type_count chart_div="collabs" chart_height=300 %} | 7 | {% include "pizza-chart.html" with chart_data=type_count chart_div="collabs" chart_height=300 %} |
| @@ -36,40 +38,41 @@ | @@ -36,40 +38,41 @@ | ||
| 36 | 38 | ||
| 37 | <ul class="unstyled-list"> | 39 | <ul class="unstyled-list"> |
| 38 | {% if user_.institution or user_.role %} | 40 | {% if user_.institution or user_.role %} |
| 39 | - <li> | ||
| 40 | - <span class="icon-briefcase icon-fixed-width"></span> | ||
| 41 | - {{ user_.role }} | ||
| 42 | - {% if user_.institution and user_.role %}-{% endif %} | ||
| 43 | - {{ user_.institution }} | ||
| 44 | - </li> | ||
| 45 | - {% endif %} | ||
| 46 | - {% if request.user.is_active %} | ||
| 47 | - <li> | ||
| 48 | - {% if user_.twitter %} | ||
| 49 | - <span class="icon-twitter icon-fixed-width"></span> <a target="_blank" href="{{ user_.twitter_link }}" title="{% trans 'Twitter account' %}">{{ user_.twitter }}</a> | ||
| 50 | - {% endif %} | ||
| 51 | - {% if user_.facebook %} | ||
| 52 | - <span class="icon-facebook icon-fixed-width"></span> <a target="_blank" href="{{ user_.facebook_link }}" title="{% trans 'Facebook account' %}">{{ user_.facebook }}</a> | ||
| 53 | - {% endif %} | ||
| 54 | - </li> | ||
| 55 | - | ||
| 56 | - {% if user_.google_talk %} | ||
| 57 | - <li><span class="icon-google-plus icon-fixed-width"></span> {{ user_.google_talk }}</li> | 41 | + <li> |
| 42 | + <span class="icon-briefcase icon-fixed-width"></span> | ||
| 43 | + {{ user_.role }} | ||
| 44 | + {% if user_.institution and user_.role %}-{% endif %} | ||
| 45 | + {{ user_.institution }} | ||
| 46 | + </li> | ||
| 58 | {% endif %} | 47 | {% endif %} |
| 59 | - {% if user_.webpage %} | ||
| 60 | - <li><span class="icon-link icon-fixed-width"></span> <a target="_blank" href="{{ user_.webpage }}" title="{% trans 'Personal webpage' %}">{{ user_.webpage }}</a></li> | 48 | + {% if request.user.is_active %} |
| 49 | + <li> | ||
| 50 | + {% if user_.twitter %} | ||
| 51 | + <span class="icon-twitter icon-fixed-width"></span> <a target="_blank" href="{{ user_.twitter_link }}" title="{% trans 'Twitter account' %}">{{ user_.twitter }}</a> | ||
| 52 | + {% endif %} | ||
| 53 | + {% if user_.facebook %} | ||
| 54 | + <span class="icon-facebook icon-fixed-width"></span> <a target="_blank" href="{{ user_.facebook_link }}" title="{% trans 'Facebook account' %}">{{ user_.facebook }}</a> | ||
| 55 | + {% endif %} | ||
| 56 | + </li> | ||
| 57 | + | ||
| 58 | + {% if user_.google_talk %} | ||
| 59 | + <li><span class="icon-google-plus icon-fixed-width"></span> {{ user_.google_talk }}</li> | ||
| 60 | + {% endif %} | ||
| 61 | + {% if user_.webpage %} | ||
| 62 | + <li><span class="icon-link icon-fixed-width"></span> <a target="_blank" href="{{ user_.webpage }}" title="{% trans 'Personal webpage' %}">{{ user_.webpage }}</a></li> | ||
| 63 | + {% endif %} | ||
| 61 | {% endif %} | 64 | {% endif %} |
| 62 | - {% endif %} | ||
| 63 | </ul> | 65 | </ul> |
| 64 | 66 | ||
| 65 | {% if user_.mailinglists %} | 67 | {% if user_.mailinglists %} |
| 66 | - <b>{% trans 'Subscribes: ' %}</b> | 68 | + <b>{% trans 'Subscribes: ' %}</b> |
| 67 | {% for list in user_.mailinglists %} | 69 | {% for list in user_.mailinglists %} |
| 68 | <span class="label label-primary">{{ list }}</span> | 70 | <span class="label label-primary">{{ list }}</span> |
| 69 | {% endfor %} | 71 | {% endfor %} |
| 70 | {% endif %} | 72 | {% endif %} |
| 71 | 73 | ||
| 72 | <div class="divider"></div> | 74 | <div class="divider"></div> |
| 75 | + | ||
| 73 | </div> | 76 | </div> |
| 74 | 77 | ||
| 75 | <div class="col-lg-4 col-md-4 col-sm-7"> | 78 | <div class="col-lg-4 col-md-4 col-sm-7"> |
| @@ -95,29 +98,50 @@ | @@ -95,29 +98,50 @@ | ||
| 95 | </div> | 98 | </div> |
| 96 | </div> | 99 | </div> |
| 97 | 100 | ||
| 98 | - </div> | ||
| 99 | - <div class="row"> | ||
| 100 | - <div class="col-lg-6 col-md-6 col-sm-12"> | ||
| 101 | - <h3>{% trans "Latest posted" %} </h3> | ||
| 102 | - <ul class="message-list"> | ||
| 103 | - {% for doc in emails %} | ||
| 104 | - {% include "message-preview.html" with result=doc %} | ||
| 105 | - {% empty %} | ||
| 106 | - <li>{% trans "There are no posts by this user so far." %}</li> | ||
| 107 | - {% endfor %} | ||
| 108 | - </ul> | ||
| 109 | - </div> | ||
| 110 | 101 | ||
| 111 | - <div class="col-lg-6 col-md-6 col-sm-12"> | ||
| 112 | - <h3>{% trans "Community inside participations" %}</h3> | ||
| 113 | - <ul class="message-list"> | ||
| 114 | - {% for result in results %} | ||
| 115 | - {% include "message-preview.html" %} | ||
| 116 | - {% empty %} | ||
| 117 | - <li>{% trans "No contributions of this user so far." %}</li> | ||
| 118 | - {% endfor %} | ||
| 119 | - </ul> | 102 | + {% if user_.badge_set.exists %} |
| 103 | + <div class="col-lg-8 col-md-12 col-sm-7"> | ||
| 104 | + <div class="panel panel-default"> | ||
| 105 | + <div class="panel-heading"> | ||
| 106 | + <h3 class="panel-title">{% trans "Badges" %}</h3> | ||
| 107 | + </div> | ||
| 108 | + <div class="panel-body"> | ||
| 109 | + <div> | ||
| 110 | + {% for badge in user_.badge_set.all %} | ||
| 111 | + {% translate badge as badge_trans %} | ||
| 112 | + <img src="{{ badge.get_badge_url }}" title="({{ badge_trans.title }}) {{ badge_trans.description }}" /> | ||
| 113 | + {% endfor %} | ||
| 114 | + </div> | ||
| 115 | + </div> | ||
| 116 | + </div> | ||
| 120 | </div> | 117 | </div> |
| 118 | + {% endif %} | ||
| 119 | + | ||
| 120 | + </div> <!-- End of user-profile row --> | ||
| 121 | + | ||
| 122 | + <div class="row"> | ||
| 123 | + | ||
| 124 | + <div class="col-lg-6 col-md-6 col-sm-12"> | ||
| 125 | + <h3>{% trans "Latest posted" %} </h3> | ||
| 126 | + <ul class="message-list"> | ||
| 127 | + {% for doc in emails %} | ||
| 128 | + {% include "message-preview.html" with result=doc %} | ||
| 129 | + {% empty %} | ||
| 130 | + <li>{% trans "There are no posts by this user so far." %}</li> | ||
| 131 | + {% endfor %} | ||
| 132 | + </ul> | ||
| 133 | + </div> | ||
| 134 | + | ||
| 135 | + <div class="col-lg-6 col-md-6 col-sm-12"> | ||
| 136 | + <h3>{% trans "Community inside participations" %}</h3> | ||
| 137 | + <ul class="message-list"> | ||
| 138 | + {% for result in results %} | ||
| 139 | + {% include "message-preview.html" %} | ||
| 140 | + {% empty %} | ||
| 141 | + <li>{% trans "No contributions of this user so far." %}</li> | ||
| 142 | + {% endfor %} | ||
| 143 | + </ul> | ||
| 144 | + </div> | ||
| 121 | 145 | ||
| 122 | </div> | 146 | </div> |
| 123 | 147 |
src/badger/admin.py
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | + | ||
| 1 | from django.contrib import admin | 3 | from django.contrib import admin |
| 4 | +from django.utils.translation import ugettext_lazy as _ | ||
| 5 | + | ||
| 6 | +from .models import Badge, BadgeI18N | ||
| 7 | + | ||
| 2 | 8 | ||
| 3 | -from .models import Badge | 9 | +class BadgeI18NInline(admin.TabularInline): |
| 10 | + model = BadgeI18N | ||
| 4 | 11 | ||
| 5 | 12 | ||
| 6 | class BadgeAdmin(admin.ModelAdmin): | 13 | class BadgeAdmin(admin.ModelAdmin): |
| 7 | - pass | 14 | + inlines = [BadgeI18NInline, ] |
| 15 | + list_display = ['title', 'description', 'order'] | ||
| 16 | + list_editable = ['order', ] | ||
| 8 | 17 | ||
| 9 | 18 | ||
| 10 | admin.site.register(Badge, BadgeAdmin) | 19 | admin.site.register(Badge, BadgeAdmin) |
| @@ -0,0 +1,44 @@ | @@ -0,0 +1,44 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | + | ||
| 3 | +from django.core.management.base import BaseCommand, CommandError | ||
| 4 | +from haystack.query import SearchQuerySet | ||
| 5 | + | ||
| 6 | +from accounts.models import User | ||
| 7 | +from badger.models import Badge | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +class Command(BaseCommand): | ||
| 11 | + help = "Rebuild the user's badges." | ||
| 12 | + | ||
| 13 | + def handle(self, *args, **kwargs): | ||
| 14 | + for badge in Badge.objects.filter(type='auto'): | ||
| 15 | + if not badge.comparison: | ||
| 16 | + continue | ||
| 17 | + elif badge.comparison == 'biggest': | ||
| 18 | + order = u'-{}'.format(Badge.USER_ATTR_OPTS[badge.user_attr]) | ||
| 19 | + sqs = SearchQuerySet().filter(type='user') | ||
| 20 | + user = sqs.order_by(order)[0] | ||
| 21 | + badge.awardees.remove(*list(badge.awardees.all())) | ||
| 22 | + badge.awardees.add(User.objects.get(pk=user.pk)) | ||
| 23 | + continue | ||
| 24 | + | ||
| 25 | + comparison = u'__{}'.format(badge.comparison) if badge.comparison \ | ||
| 26 | + is not 'equal' else u'' | ||
| 27 | + | ||
| 28 | + key = u'{}{}'.format( | ||
| 29 | + Badge.USER_ATTR_OPTS[badge.user_attr], | ||
| 30 | + comparison | ||
| 31 | + ) | ||
| 32 | + opts = {key: badge.value} | ||
| 33 | + | ||
| 34 | + sqs = SearchQuerySet().filter( | ||
| 35 | + type='user', | ||
| 36 | + **opts | ||
| 37 | + ) | ||
| 38 | + | ||
| 39 | + # Remove all awardees to make sure that all of then | ||
| 40 | + # still accomplish the necessary to keep the badge | ||
| 41 | + badge.awardees.remove(*list(badge.awardees.all())) | ||
| 42 | + | ||
| 43 | + for user in sqs: | ||
| 44 | + badge.awardees.add(User.objects.get(pk=user.pk)) |
src/badger/management/commands/update_badges.py
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
| 2 | 2 | ||
| 3 | from django.core.management.base import BaseCommand, CommandError | 3 | from django.core.management.base import BaseCommand, CommandError |
| 4 | +from haystack.query import SearchQuerySet | ||
| 4 | 5 | ||
| 5 | from accounts.models import User | 6 | from accounts.models import User |
| 6 | -from badger.utils import get_counters_to_badge | ||
| 7 | from badger.models import Badge | 7 | from badger.models import Badge |
| 8 | 8 | ||
| 9 | 9 | ||
| @@ -14,17 +14,26 @@ class Command(BaseCommand): | @@ -14,17 +14,26 @@ class Command(BaseCommand): | ||
| 14 | for badge in Badge.objects.filter(type='auto'): | 14 | for badge in Badge.objects.filter(type='auto'): |
| 15 | if not badge.comparison: | 15 | if not badge.comparison: |
| 16 | continue | 16 | continue |
| 17 | - for user in User.objects.all(): | ||
| 18 | - user_counters = get_counters_to_badge(user) | ||
| 19 | - | ||
| 20 | - # TODO remove user if it doesn't sastify the conditions of the | ||
| 21 | - # badge anymore | ||
| 22 | - if badge.comparison == 'gte': | ||
| 23 | - if user_counters[badge.user_attr] >= badge.value: | ||
| 24 | - badge.awardees.add(user) | ||
| 25 | - elif badge.comparison == 'lte': | ||
| 26 | - if user_counters[badge.user_attr] <= badge.value: | ||
| 27 | - badge.awardees.add(user) | ||
| 28 | - elif badge.comparison == 'equal': | ||
| 29 | - if user_counters[badge.user_attr] == badge.value: | ||
| 30 | - badge.awardees.add(user) | 17 | + elif badge.comparison == 'biggest': |
| 18 | + order = u'-{}'.format(Badge.USER_ATTR_OPTS[badge.user_attr]) | ||
| 19 | + sqs = SearchQuerySet().filter(type='user') | ||
| 20 | + user = sqs.order_by(order)[0] | ||
| 21 | + badge.awardees.add(User.objects.get(pk=user.pk)) | ||
| 22 | + continue | ||
| 23 | + | ||
| 24 | + comparison = u'__{}'.format(badge.comparison) if badge.comparison \ | ||
| 25 | + is not 'equal' else u'' | ||
| 26 | + | ||
| 27 | + key = u'{}{}'.format( | ||
| 28 | + Badge.USER_ATTR_OPTS[badge.user_attr], | ||
| 29 | + comparison | ||
| 30 | + ) | ||
| 31 | + opts = {key: badge.value} | ||
| 32 | + | ||
| 33 | + sqs = SearchQuerySet().filter( | ||
| 34 | + type='user', | ||
| 35 | + **opts | ||
| 36 | + ) | ||
| 37 | + | ||
| 38 | + for user in sqs: | ||
| 39 | + badge.awardees.add(User.objects.get(pk=user.pk)) |
src/badger/migrations/0002_auto__add_field_badge_title_en__add_field_badge_title_es__add_field_ba.py
0 → 100644
| @@ -0,0 +1,120 @@ | @@ -0,0 +1,120 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | +import datetime | ||
| 3 | +from south.db import db | ||
| 4 | +from south.v2 import SchemaMigration | ||
| 5 | +from django.db import models | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class Migration(SchemaMigration): | ||
| 9 | + | ||
| 10 | + def forwards(self, orm): | ||
| 11 | + # Adding field 'Badge.title_en' | ||
| 12 | + db.add_column(u'badger_badge', 'title_en', | ||
| 13 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 14 | + keep_default=False) | ||
| 15 | + | ||
| 16 | + # Adding field 'Badge.title_es' | ||
| 17 | + db.add_column(u'badger_badge', 'title_es', | ||
| 18 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 19 | + keep_default=False) | ||
| 20 | + | ||
| 21 | + # Adding field 'Badge.description_en' | ||
| 22 | + db.add_column(u'badger_badge', 'description_en', | ||
| 23 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 24 | + keep_default=False) | ||
| 25 | + | ||
| 26 | + # Adding field 'Badge.description_es' | ||
| 27 | + db.add_column(u'badger_badge', 'description_es', | ||
| 28 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 29 | + keep_default=False) | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + # Changing field 'Badge.description' | ||
| 33 | + db.alter_column(u'badger_badge', 'description', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)) | ||
| 34 | + | ||
| 35 | + # Changing field 'Badge.title' | ||
| 36 | + db.alter_column(u'badger_badge', 'title', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)) | ||
| 37 | + | ||
| 38 | + def backwards(self, orm): | ||
| 39 | + # Deleting field 'Badge.title_en' | ||
| 40 | + db.delete_column(u'badger_badge', 'title_en') | ||
| 41 | + | ||
| 42 | + # Deleting field 'Badge.title_es' | ||
| 43 | + db.delete_column(u'badger_badge', 'title_es') | ||
| 44 | + | ||
| 45 | + # Deleting field 'Badge.description_en' | ||
| 46 | + db.delete_column(u'badger_badge', 'description_en') | ||
| 47 | + | ||
| 48 | + # Deleting field 'Badge.description_es' | ||
| 49 | + db.delete_column(u'badger_badge', 'description_es') | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + # Changing field 'Badge.description' | ||
| 53 | + db.alter_column(u'badger_badge', 'description', self.gf('django.db.models.fields.CharField')(default='', max_length=200)) | ||
| 54 | + | ||
| 55 | + # Changing field 'Badge.title' | ||
| 56 | + db.alter_column(u'badger_badge', 'title', self.gf('django.db.models.fields.CharField')(default='', max_length=200)) | ||
| 57 | + | ||
| 58 | + models = { | ||
| 59 | + u'accounts.user': { | ||
| 60 | + 'Meta': {'object_name': 'User'}, | ||
| 61 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 62 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | ||
| 63 | + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 64 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 65 | + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), | ||
| 66 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | ||
| 67 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 68 | + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 69 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
| 70 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 71 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 72 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 73 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 74 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
| 75 | + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 76 | + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 77 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | ||
| 78 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), | ||
| 79 | + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), | ||
| 80 | + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) | ||
| 81 | + }, | ||
| 82 | + u'auth.group': { | ||
| 83 | + 'Meta': {'object_name': 'Group'}, | ||
| 84 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 85 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
| 86 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
| 87 | + }, | ||
| 88 | + u'auth.permission': { | ||
| 89 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | ||
| 90 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 91 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
| 92 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 93 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
| 94 | + }, | ||
| 95 | + u'badger.badge': { | ||
| 96 | + 'Meta': {'object_name': 'Badge'}, | ||
| 97 | + 'awardees': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['accounts.User']", 'null': 'True', 'blank': 'True'}), | ||
| 98 | + 'comparison': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), | ||
| 99 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 100 | + 'description_en': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 101 | + 'description_es': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 102 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 103 | + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), | ||
| 104 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 105 | + 'title_en': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 106 | + 'title_es': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 107 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '200'}), | ||
| 108 | + 'user_attr': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), | ||
| 109 | + 'value': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) | ||
| 110 | + }, | ||
| 111 | + u'contenttypes.contenttype': { | ||
| 112 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
| 113 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 114 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 115 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 116 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + complete_apps = ['badger'] | ||
| 0 | \ No newline at end of file | 121 | \ No newline at end of file |
src/badger/migrations/0003_auto__add_badgei18n__add_unique_badgei18n_i18n_source_i18n_language__d.py
0 → 100644
| @@ -0,0 +1,131 @@ | @@ -0,0 +1,131 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | +import datetime | ||
| 3 | +from south.db import db | ||
| 4 | +from south.v2 import SchemaMigration | ||
| 5 | +from django.db import models | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class Migration(SchemaMigration): | ||
| 9 | + | ||
| 10 | + def forwards(self, orm): | ||
| 11 | + # Adding model 'BadgeI18N' | ||
| 12 | + db.create_table(u'badger_badgei18n', ( | ||
| 13 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
| 14 | + ('i18n_language', self.gf('django.db.models.fields.CharField')(max_length=10)), | ||
| 15 | + ('title', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)), | ||
| 16 | + ('description', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)), | ||
| 17 | + ('i18n_source', self.gf('django.db.models.fields.related.ForeignKey')(related_name='translations', to=orm['badger.Badge'])), | ||
| 18 | + )) | ||
| 19 | + db.send_create_signal(u'badger', ['BadgeI18N']) | ||
| 20 | + | ||
| 21 | + # Adding unique constraint on 'BadgeI18N', fields ['i18n_source', 'i18n_language'] | ||
| 22 | + db.create_unique(u'badger_badgei18n', ['i18n_source_id', 'i18n_language']) | ||
| 23 | + | ||
| 24 | + # Deleting field 'Badge.title_en' | ||
| 25 | + db.delete_column(u'badger_badge', 'title_en') | ||
| 26 | + | ||
| 27 | + # Deleting field 'Badge.description_en' | ||
| 28 | + db.delete_column(u'badger_badge', 'description_en') | ||
| 29 | + | ||
| 30 | + # Deleting field 'Badge.title_es' | ||
| 31 | + db.delete_column(u'badger_badge', 'title_es') | ||
| 32 | + | ||
| 33 | + # Deleting field 'Badge.description_es' | ||
| 34 | + db.delete_column(u'badger_badge', 'description_es') | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + def backwards(self, orm): | ||
| 38 | + # Removing unique constraint on 'BadgeI18N', fields ['i18n_source', 'i18n_language'] | ||
| 39 | + db.delete_unique(u'badger_badgei18n', ['i18n_source_id', 'i18n_language']) | ||
| 40 | + | ||
| 41 | + # Deleting model 'BadgeI18N' | ||
| 42 | + db.delete_table(u'badger_badgei18n') | ||
| 43 | + | ||
| 44 | + # Adding field 'Badge.title_en' | ||
| 45 | + db.add_column(u'badger_badge', 'title_en', | ||
| 46 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 47 | + keep_default=False) | ||
| 48 | + | ||
| 49 | + # Adding field 'Badge.description_en' | ||
| 50 | + db.add_column(u'badger_badge', 'description_en', | ||
| 51 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 52 | + keep_default=False) | ||
| 53 | + | ||
| 54 | + # Adding field 'Badge.title_es' | ||
| 55 | + db.add_column(u'badger_badge', 'title_es', | ||
| 56 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 57 | + keep_default=False) | ||
| 58 | + | ||
| 59 | + # Adding field 'Badge.description_es' | ||
| 60 | + db.add_column(u'badger_badge', 'description_es', | ||
| 61 | + self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), | ||
| 62 | + keep_default=False) | ||
| 63 | + | ||
| 64 | + | ||
| 65 | + models = { | ||
| 66 | + u'accounts.user': { | ||
| 67 | + 'Meta': {'object_name': 'User'}, | ||
| 68 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 69 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | ||
| 70 | + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 71 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 72 | + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), | ||
| 73 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | ||
| 74 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 75 | + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 76 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
| 77 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 78 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 79 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 80 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 81 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
| 82 | + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 83 | + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 84 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | ||
| 85 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), | ||
| 86 | + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), | ||
| 87 | + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) | ||
| 88 | + }, | ||
| 89 | + u'auth.group': { | ||
| 90 | + 'Meta': {'object_name': 'Group'}, | ||
| 91 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 92 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
| 93 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
| 94 | + }, | ||
| 95 | + u'auth.permission': { | ||
| 96 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | ||
| 97 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 98 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
| 99 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 100 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
| 101 | + }, | ||
| 102 | + u'badger.badge': { | ||
| 103 | + 'Meta': {'object_name': 'Badge'}, | ||
| 104 | + 'awardees': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['accounts.User']", 'null': 'True', 'blank': 'True'}), | ||
| 105 | + 'comparison': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), | ||
| 106 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 107 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 108 | + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), | ||
| 109 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 110 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '200'}), | ||
| 111 | + 'user_attr': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), | ||
| 112 | + 'value': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) | ||
| 113 | + }, | ||
| 114 | + u'badger.badgei18n': { | ||
| 115 | + 'Meta': {'unique_together': "(('i18n_source', 'i18n_language'),)", 'object_name': 'BadgeI18N'}, | ||
| 116 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 117 | + 'i18n_language': ('django.db.models.fields.CharField', [], {'max_length': '10'}), | ||
| 118 | + 'i18n_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'to': u"orm['badger.Badge']"}), | ||
| 119 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 120 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) | ||
| 121 | + }, | ||
| 122 | + u'contenttypes.contenttype': { | ||
| 123 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
| 124 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 125 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 126 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 127 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + complete_apps = ['badger'] | ||
| 0 | \ No newline at end of file | 132 | \ No newline at end of file |
src/badger/migrations/0004_auto__add_field_badge_order.py
0 → 100644
| @@ -0,0 +1,89 @@ | @@ -0,0 +1,89 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | ||
| 2 | +import datetime | ||
| 3 | +from south.db import db | ||
| 4 | +from south.v2 import SchemaMigration | ||
| 5 | +from django.db import models | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +class Migration(SchemaMigration): | ||
| 9 | + | ||
| 10 | + def forwards(self, orm): | ||
| 11 | + # Adding field 'Badge.order' | ||
| 12 | + db.add_column(u'badger_badge', 'order', | ||
| 13 | + self.gf('django.db.models.fields.PositiveSmallIntegerField')(default=100), | ||
| 14 | + keep_default=False) | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + def backwards(self, orm): | ||
| 18 | + # Deleting field 'Badge.order' | ||
| 19 | + db.delete_column(u'badger_badge', 'order') | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + models = { | ||
| 23 | + u'accounts.user': { | ||
| 24 | + 'Meta': {'object_name': 'User'}, | ||
| 25 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 26 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | ||
| 27 | + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 28 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 29 | + 'google_talk': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), | ||
| 30 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | ||
| 31 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 32 | + 'institution': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 33 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
| 34 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 35 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 36 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 37 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 38 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
| 39 | + 'role': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 40 | + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
| 41 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | ||
| 42 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), | ||
| 43 | + 'verification_hash': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), | ||
| 44 | + 'webpage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}) | ||
| 45 | + }, | ||
| 46 | + u'auth.group': { | ||
| 47 | + 'Meta': {'object_name': 'Group'}, | ||
| 48 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 49 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
| 50 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
| 51 | + }, | ||
| 52 | + u'auth.permission': { | ||
| 53 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | ||
| 54 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 55 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
| 56 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 57 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
| 58 | + }, | ||
| 59 | + u'badger.badge': { | ||
| 60 | + 'Meta': {'ordering': "['order']", 'object_name': 'Badge'}, | ||
| 61 | + 'awardees': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['accounts.User']", 'null': 'True', 'blank': 'True'}), | ||
| 62 | + 'comparison': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), | ||
| 63 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 64 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 65 | + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), | ||
| 66 | + 'order': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '100'}), | ||
| 67 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 68 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '200'}), | ||
| 69 | + 'user_attr': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), | ||
| 70 | + 'value': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) | ||
| 71 | + }, | ||
| 72 | + u'badger.badgei18n': { | ||
| 73 | + 'Meta': {'unique_together': "(('i18n_source', 'i18n_language'),)", 'object_name': 'BadgeI18N'}, | ||
| 74 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), | ||
| 75 | + 'i18n_language': ('django.db.models.fields.CharField', [], {'max_length': '10'}), | ||
| 76 | + 'i18n_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'to': u"orm['badger.Badge']"}), | ||
| 77 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 78 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) | ||
| 79 | + }, | ||
| 80 | + u'contenttypes.contenttype': { | ||
| 81 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
| 82 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 83 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 84 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 85 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + complete_apps = ['badger'] | ||
| 0 | \ No newline at end of file | 90 | \ No newline at end of file |
src/badger/models.py
| 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
| 2 | 2 | ||
| 3 | +from django.conf import settings | ||
| 3 | from django.contrib.auth import get_user_model | 4 | from django.contrib.auth import get_user_model |
| 4 | from django.db import models | 5 | from django.db import models |
| 5 | -from django.utils.translation import ugettext as _ | 6 | +from django.utils.translation import ugettext_lazy as _ |
| 7 | +from PIL import Image | ||
| 8 | +from i18n_model.models import I18nModel | ||
| 6 | 9 | ||
| 7 | 10 | ||
| 8 | class Badge(models.Model): | 11 | class Badge(models.Model): |
| @@ -10,6 +13,7 @@ class Badge(models.Model): | @@ -10,6 +13,7 @@ class Badge(models.Model): | ||
| 10 | (u'gte', _(u'Greater than or equal')), | 13 | (u'gte', _(u'Greater than or equal')), |
| 11 | (u'lte', _(u'less than or equal')), | 14 | (u'lte', _(u'less than or equal')), |
| 12 | (u'equal', _(u'Equal')), | 15 | (u'equal', _(u'Equal')), |
| 16 | + (u'biggest', _(u'Biggest')), | ||
| 13 | ) | 17 | ) |
| 14 | TYPE_CHOICES = ( | 18 | TYPE_CHOICES = ( |
| 15 | (u'auto', _(u'Automatically')), | 19 | (u'auto', _(u'Automatically')), |
| @@ -22,8 +26,18 @@ class Badge(models.Model): | @@ -22,8 +26,18 @@ class Badge(models.Model): | ||
| 22 | (u'revisions', _(u'Revisions')), | 26 | (u'revisions', _(u'Revisions')), |
| 23 | (u'tickets', _(u'Ticket')), | 27 | (u'tickets', _(u'Ticket')), |
| 24 | ) | 28 | ) |
| 25 | - title = models.CharField(_(u'Title'), max_length=200) | ||
| 26 | - description = models.CharField(_(u'Description'), max_length=200) | 29 | + USER_ATTR_OPTS = { |
| 30 | + u'messages': u'message_count', | ||
| 31 | + u'revisions': u'changeset_count', | ||
| 32 | + u'tickets': u'ticket_count', | ||
| 33 | + u'wikis': u'wiki_count', | ||
| 34 | + u'contributions': u'contribution_count', | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + title = models.CharField(_(u'Title'), max_length=200, blank=True, | ||
| 38 | + null=True) | ||
| 39 | + description = models.CharField(_(u'Description'), max_length=200, | ||
| 40 | + blank=True, null=True) | ||
| 27 | image = models.ImageField(upload_to='badges') | 41 | image = models.ImageField(upload_to='badges') |
| 28 | type = models.CharField(_(u'Type'), max_length=200, choices=TYPE_CHOICES) | 42 | type = models.CharField(_(u'Type'), max_length=200, choices=TYPE_CHOICES) |
| 29 | user_attr = models.CharField( | 43 | user_attr = models.CharField( |
| @@ -50,10 +64,22 @@ class Badge(models.Model): | @@ -50,10 +64,22 @@ class Badge(models.Model): | ||
| 50 | blank=True, | 64 | blank=True, |
| 51 | null=True | 65 | null=True |
| 52 | ) | 66 | ) |
| 67 | + order = models.PositiveSmallIntegerField(_(u'Order'), default=100) | ||
| 53 | 68 | ||
| 54 | class Meta: | 69 | class Meta: |
| 55 | verbose_name = _(u'Badge') | 70 | verbose_name = _(u'Badge') |
| 56 | verbose_name_plural = _(u'Badges') | 71 | verbose_name_plural = _(u'Badges') |
| 72 | + ordering = ['order', ] | ||
| 73 | + | ||
| 74 | + def get_badge_url(self): | ||
| 75 | + return u'{}{}'.format(settings.MEDIA_URL, self.image) | ||
| 76 | + | ||
| 77 | + def save(self, *args, **kwargs): | ||
| 78 | + img = Image.open(self.image) | ||
| 79 | + (width, height) = img.size | ||
| 80 | + img = img.resize((50, 50), Image.ANTIALIAS) | ||
| 81 | + super(Badge, self).save(*args, **kwargs) | ||
| 82 | + img.save(self.image.path) | ||
| 57 | 83 | ||
| 58 | def __unicode__(self): | 84 | def __unicode__(self): |
| 59 | return u'{} ({}, {})'.format( | 85 | return u'{} ({}, {})'.format( |
| @@ -61,3 +87,9 @@ class Badge(models.Model): | @@ -61,3 +87,9 @@ class Badge(models.Model): | ||
| 61 | self.get_user_attr_display(), | 87 | self.get_user_attr_display(), |
| 62 | self.get_type_display(), | 88 | self.get_type_display(), |
| 63 | ) | 89 | ) |
| 90 | + | ||
| 91 | + | ||
| 92 | +class BadgeI18N(I18nModel): | ||
| 93 | + class Meta: | ||
| 94 | + source_model = Badge | ||
| 95 | + translation_fields = ('title', 'description') |
src/badger/utils.py
| @@ -2,32 +2,45 @@ | @@ -2,32 +2,45 @@ | ||
| 2 | 2 | ||
| 3 | from django.db.models import Count | 3 | from django.db.models import Count |
| 4 | 4 | ||
| 5 | -from haystack.query import SearchQuerySet | ||
| 6 | - | ||
| 7 | from proxy.models import Revision, Ticket, Wiki | 5 | from proxy.models import Revision, Ticket, Wiki |
| 8 | -from super_archives.models import Message | ||
| 9 | - | ||
| 10 | - | ||
| 11 | -def get_counters_to_badge(user): | ||
| 12 | - # count_revisions = Revision.objects.filter(author=user.username).count() | ||
| 13 | - # count_tickets = Ticket.objects.filter(author=user.username).count() | ||
| 14 | - # count_wikis = Wiki.objects.filter(author=user.username).count() | ||
| 15 | - count_revisions = SearchQuerySet().filter( | ||
| 16 | - type='changeset', | ||
| 17 | - author=user.username | ||
| 18 | - ).count() | ||
| 19 | - count_tickets = SearchQuerySet().filter( | ||
| 20 | - type='ticket', | ||
| 21 | - author=user.username | ||
| 22 | - ).count() | ||
| 23 | - count_wikis = SearchQuerySet().filter( | ||
| 24 | - type='wiki', | ||
| 25 | - author=user.username | ||
| 26 | - ).count() | ||
| 27 | - return dict( | ||
| 28 | - messages=user.emails.aggregate(Count('message'))['message__count'], | ||
| 29 | - revisions=count_revisions, | ||
| 30 | - tickets=count_tickets, | ||
| 31 | - wikis=count_wikis, | ||
| 32 | - contributions=count_revisions + count_tickets + count_wikis, | ||
| 33 | - ) | 6 | +from accounts.models import User |
| 7 | + | ||
| 8 | + | ||
| 9 | +def get_wiki_counters(): | ||
| 10 | + return { | ||
| 11 | + author: count for author, count in Wiki.objects.values_list( | ||
| 12 | + 'author' | ||
| 13 | + ).annotate(count=Count('author')) | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +def get_revision_counters(): | ||
| 18 | + return { | ||
| 19 | + author: count for author, count in Revision.objects.values_list( | ||
| 20 | + 'author' | ||
| 21 | + ).annotate(count=Count('author')) | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +def get_ticket_counters(): | ||
| 26 | + return { | ||
| 27 | + author: count for author, count in Ticket.objects.values_list( | ||
| 28 | + 'author' | ||
| 29 | + ).annotate(count=Count('author')) | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +def get_users_counters(): | ||
| 34 | + wiki_counters = get_wiki_counters() | ||
| 35 | + revision_counters = get_revision_counters() | ||
| 36 | + ticket_counters = get_ticket_counters() | ||
| 37 | + | ||
| 38 | + users_counters = {} | ||
| 39 | + for user in User.objects.annotate(message_count=Count('emails__message')): | ||
| 40 | + users_counters[user.username] = { | ||
| 41 | + 'messages': user.message_count, | ||
| 42 | + 'wikis': wiki_counters.get(user.username, 0), | ||
| 43 | + 'revisions': revision_counters.get(user.username, 0), | ||
| 44 | + 'tickets': ticket_counters.get(user.username, 0), | ||
| 45 | + } | ||
| 46 | + return users_counters |
src/colab/custom_settings.py
| @@ -9,8 +9,8 @@ TIME_ZONE = 'America/Sao_Paulo' | @@ -9,8 +9,8 @@ TIME_ZONE = 'America/Sao_Paulo' | ||
| 9 | gettext = lambda s: s | 9 | gettext = lambda s: s |
| 10 | LANGUAGES = ( | 10 | LANGUAGES = ( |
| 11 | ('en', gettext('English')), | 11 | ('en', gettext('English')), |
| 12 | - ('es', gettext('Spanish')), | ||
| 13 | ('pt-br', gettext('Portuguese')), | 12 | ('pt-br', gettext('Portuguese')), |
| 13 | + ('es', gettext('Spanish')), | ||
| 14 | ) | 14 | ) |
| 15 | 15 | ||
| 16 | DJANGO_DATE_FORMAT_TO_JS = { | 16 | DJANGO_DATE_FORMAT_TO_JS = { |
| @@ -18,7 +18,7 @@ DJANGO_DATE_FORMAT_TO_JS = { | @@ -18,7 +18,7 @@ DJANGO_DATE_FORMAT_TO_JS = { | ||
| 18 | 'es': ('es', 'dd/MM/yyyy'), | 18 | 'es': ('es', 'dd/MM/yyyy'), |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | -LANGUAGE_CODE = 'pt-br' | 21 | +LANGUAGE_CODE = 'en' |
| 22 | 22 | ||
| 23 | # The absolute path to the folder containing the attachments | 23 | # The absolute path to the folder containing the attachments |
| 24 | ATTACHMENTS_FOLDER_PATH = '/home/colab/trac/attachments/' | 24 | ATTACHMENTS_FOLDER_PATH = '/home/colab/trac/attachments/' |
| @@ -108,7 +108,7 @@ INSTALLED_APPS = INSTALLED_APPS + ( | @@ -108,7 +108,7 @@ INSTALLED_APPS = INSTALLED_APPS + ( | ||
| 108 | 'conversejs', | 108 | 'conversejs', |
| 109 | 'haystack', | 109 | 'haystack', |
| 110 | 'hitcounter', | 110 | 'hitcounter', |
| 111 | - 'badger', | 111 | + 'i18n_model', |
| 112 | 112 | ||
| 113 | # Own apps | 113 | # Own apps |
| 114 | 'super_archives', | 114 | 'super_archives', |
| @@ -118,6 +118,7 @@ INSTALLED_APPS = INSTALLED_APPS + ( | @@ -118,6 +118,7 @@ INSTALLED_APPS = INSTALLED_APPS + ( | ||
| 118 | 'accounts', | 118 | 'accounts', |
| 119 | 'proxy', | 119 | 'proxy', |
| 120 | 'search', | 120 | 'search', |
| 121 | + 'badger', | ||
| 121 | 122 | ||
| 122 | # Feedzilla and deps | 123 | # Feedzilla and deps |
| 123 | 'feedzilla', | 124 | 'feedzilla', |
| @@ -231,6 +232,7 @@ STATICFILES_DIRS = ( | @@ -231,6 +232,7 @@ STATICFILES_DIRS = ( | ||
| 231 | ) | 232 | ) |
| 232 | 233 | ||
| 233 | STATIC_ROOT = os.path.join(BASE_DIR, '..', 'www', 'static') | 234 | STATIC_ROOT = os.path.join(BASE_DIR, '..', 'www', 'static') |
| 235 | +MEDIA_ROOT = os.path.join(BASE_DIR, '..', 'www', 'static', 'media') | ||
| 234 | 236 | ||
| 235 | TEMPLATE_DIRS = ( | 237 | TEMPLATE_DIRS = ( |
| 236 | os.path.join(BASE_DIR, 'templates'), | 238 | os.path.join(BASE_DIR, 'templates'), |
src/colab/settings.py
| @@ -80,5 +80,6 @@ USE_TZ = True | @@ -80,5 +80,6 @@ USE_TZ = True | ||
| 80 | # https://docs.djangoproject.com/en/dev/howto/static-files/ | 80 | # https://docs.djangoproject.com/en/dev/howto/static-files/ |
| 81 | 81 | ||
| 82 | STATIC_URL = '/static/' | 82 | STATIC_URL = '/static/' |
| 83 | +MEDIA_URL = '/media/' | ||
| 83 | 84 | ||
| 84 | from custom_settings import * | 85 | from custom_settings import * |
src/colab/urls.py
| 1 | -from django.conf.urls import patterns, include, url | 1 | +from django.conf.urls import patterns, include, url, static |
| 2 | from django.conf import settings | 2 | from django.conf import settings |
| 3 | from django.views.generic import TemplateView | 3 | from django.views.generic import TemplateView |
| 4 | from django.contrib import admin | 4 | from django.contrib import admin |
| @@ -31,3 +31,9 @@ urlpatterns = patterns('', | @@ -31,3 +31,9 @@ urlpatterns = patterns('', | ||
| 31 | 31 | ||
| 32 | url(r'^', include('proxy.urls')), | 32 | url(r'^', include('proxy.urls')), |
| 33 | ) | 33 | ) |
| 34 | + | ||
| 35 | +if settings.DEBUG: | ||
| 36 | + urlpatterns += static.static( | ||
| 37 | + settings.MEDIA_URL, | ||
| 38 | + document_root=settings.MEDIA_ROOT | ||
| 39 | + ) |