瀏覽代碼

Translate homepage. Optimise graphs generation.

Camille Masset 9 年之前
父節點
當前提交
dc66827e5c
共有 6 個文件被更改,包括 507 次插入187 次删除
  1. 1 1
      counter/models.py
  2. 99 74
      counter/templates/homeTemplate.html
  3. 108 86
      counter/views/home.py
  4. 146 3
      locale/en/LC_MESSAGES/django.po
  5. 149 21
      locale/fr/LC_MESSAGES/django.po
  6. 4 2
      requirements.txt

+ 1 - 1
counter/models.py

@@ -31,7 +31,7 @@ class Reset(models.Model):
31 31
 
32 32
     def __str__(self):
33 33
         if self.who is None or self.who == self.counter:
34
-            return _('%(counter)s : %(datetime)s (%(reason)s)') % {
34
+            return _('%(counter)s: %(datetime)s (%(reason)s)') % {
35 35
                 'counter': self.counter,
36 36
                 'datetime': arrow.Arrow.fromdatetime(self.timestamp).humanize(locale=(get_language() or 'en')), # dirty hack...
37 37
                 'reason': self.reason

+ 99 - 74
counter/templates/homeTemplate.html

@@ -1,9 +1,17 @@
1
-{% extends 'baseTemplate.html' %} {% block title %}Compteurs{% endblock %} {% block content %} {% load hashtags %}
1
+{% extends 'baseTemplate.html' %}
2
+
3
+{% load i18n %}
4
+{% load hashtags %}
5
+
6
+{% block title %}{% trans "Counters" %}{% endblock %}
7
+
8
+{% block content %}
2 9
 <div class="text-center">
3 10
 	<h1><a class="counter-link" href="{% url 'home' %}">SeumBook™</a></h1>
4 11
 </div>
5 12
 <div class="container-fluid">
6 13
 	<div class="row" id="my-counter">
14
+		{# my counter #}
7 15
 		<div class="col-sm-6">
8 16
 			<div class="panel panel-primary">
9 17
 				<div class="panel-heading">
@@ -21,60 +29,64 @@
21 29
 				<div class="primary-counter panel-body" id="container{{myCounter.id}}">
22 30
 					<div style="width:100%;">
23 31
 						{% if myCounter.lastReset.noSeum %}
24
-						<strong>N'a pas encore eu le seum.</strong>
25
-						<br> {% else %}
26
-						<strong>
27
-						{% if myCounter.lastReset.selfSeum %}
28
-						J'ai eu le seum il y a {{ myCounter.lastReset.formatted_delta }}.
32
+							<strong>{% trans "Has not got the seum yet" %}.</strong><br />
29 33
 						{% else %}
30
-						{{myCounter.lastReset.who.trigramme}} m'a foutu le seum il y a {{ myCounter.lastReset.formatted_delta }}.
34
+							<strong>
35
+								{% if myCounter.lastReset.selfSeum %}
36
+									{% trans "I got the seum" %} {{ myCounter.lastReset.formatted_delta }}.
37
+								{% else %}
38
+									{{ myCounter.lastReset.who.trigramme }} {% trans "threw me the seum"} {{ myCounter.lastReset.formatted_delta }}.
39
+								{% endif %}
40
+							</strong><br />
31 41
 						{% endif %}
32
-					</strong>
33
-						<br> {% endif %}
34 42
 						<p>{{ myCounter.lastReset.reason | hashtag }}</p>
35 43
 						<div class="text-center" id="button{{myCounter.id}}">
36
-							<button class="btn btn-default btn-danger" type="button" onclick="revealSeumForm({{myCounter.id}})">Remettre à zéro</button>
44
+							<button class="btn btn-default btn-danger" type="button" onclick="revealSeumForm({{myCounter.id}})">
45
+								{% trans "Reset" %}
46
+							</button>
37 47
 						</div>
48
+						{# reset form for my counter #}
38 49
 						<form style="display:none" id="counter{{myCounter.id}}" action="{% url 'reset-counter' %}" method="post">
39 50
 							{% csrf_token %}
40 51
 							<div class="form-group">
41
-								<label for="reason">Motif du seum</label>
52
+								<label for="reason">{% trans "Motive of the seum" %}</label>
42 53
 								<input id="reason{{myCounter.id}}" type="text" class="form-control" name="reason"></input>
43 54
 							</div>
44 55
 							<input type="hidden" name="counter" value="{{myCounter.id}}"></input>
45 56
 							<input type="hidden" name="redirect" value="{% url 'home' %}"></input>
46 57
 							<input type="hidden" name="who" value="{{myCounter.id}}"></input>
47 58
 							<div class="text-center">
48
-								<button type="submit" class="btn btn-default btn-success">J'ai le seum</button>
59
+								<button type="submit" class="btn btn-default btn-success">{% trans "I've got the seum" %}</button>
49 60
 							</div>
50 61
 						</form>
51 62
 					</div>
52 63
 				</div>
53 64
 			</div>
54 65
 		</div>
66
+		{# QuickSeum #}
55 67
 		<div class="col-sm-6">
56 68
 			<div class="panel panel-primary">
57 69
 				<div class="panel-heading">
58
-					<h2 class="panel-title"><b>QuickSeum™</b> <small>Brise le mur du seum</small></h2>
70
+					<h2 class="panel-title"><b>QuickSeum™</b> <small>{% trans "Break the seum wall" %}</small></h2>
59 71
 				</div>
60 72
 				<div class="primary-counter panel-body">
61 73
 					<form class="form-horizontal" action="{% url 'reset-counter'%}" method="POST" style="width:100%;">
62 74
 						{% csrf_token %}
63 75
 						<div class="form-group">
64
-							<label for="id_quicktrigramme" class="col-sm-3 control-label">Trigramme</label>
76
+							<label for="id_quicktrigramme" class="col-sm-3 control-label">{% trans "Trigram" %}</label>
65 77
 							<div class="col-sm-9">
66 78
 								<input id="id_quicktrigramme" maxlength="3" type="text" class="form-control text-uppercase" name="trigramme" onkeyup="this.value=this.value.toUpperCase();" required />
67 79
 							</div>
68 80
 						</div>
69 81
 						<div class="form-group">
70
-							<label for="id_quickreason" class="col-sm-3 control-label">Motif</label>
82
+							<label for="id_quickreason" class="col-sm-3 control-label">{% trans "Motive" %}</label>
71 83
 							<div class="col-sm-9">
72 84
 								<input type="text" class="form-control" id="id_quickreason" name="reason" />
73 85
 							</div>
74 86
 						</div>
75 87
 						<div class="form-group">
76 88
 							<div class="col-sm-offset-3 col-sm-9">
77
-								<button type="submit" class="btn btn-danger">Foutre le seum</button>
89
+								<button type="submit" class="btn btn-danger">{% trans "Throw the seum" %}</button>
78 90
 							</div>
79 91
 						</div>
80 92
 						<input type="hidden" name="who" value="{{myCounter.id}}"></input>
@@ -84,6 +96,7 @@
84 96
 			</div>
85 97
 		</div>
86 98
 	</div>
99
+	{# Counters panel #}
87 100
 	<div class="row">
88 101
 		{% for counter in counters %}
89 102
 		<div class="col-md-4 col-sm-6 col-lg-3">
@@ -99,54 +112,56 @@
99 112
 								<b>{{ counter.trigramme }}</b> <small>{{ counter.name }}</small>
100 113
 							</a>
101 114
 							{% if not counter.lastReset.noSeum %}
102
-							{% if counter.alreadyLiked %}
103
-							<span class="pull-right badge" {% if counter.likeCount > 0 %} data-toggle="tooltip" data-placement="top" title="{{ counter.likersString }}" {% endif %}>
104
-								<span class="glyphicon glyphicon-ok"></span>&emsp;{{ counter.likeCount }}
105
-							</span>
106
-							{% elif counter.id == myCounter.id or counter.lastReset.who.id == myCounter.id %}
107
-			                <span class="pull-right badge" {% if counter.likeCount > 0 %} data-toggle="tooltip" data-placement="top" title="{{ counter.likersString }}" {% endif %}>
108
-			                    <span class="glyphicon glyphicon-heart"></span>&emsp;{{ counter.likeCount }}
109
-			                </span>
110
-							{% else %}
111
-							<a class="pull-right badge" onclick="document.forms['like{{counter.id}}'].submit();">
112
-								<span class="glyphicon glyphicon-heart"></span>&emsp;{{ counter.likeCount }}
113
-							</a>
114
-							{% endif %}
115
+								{% if counter.alreadyLiked %}
116
+									<span class="pull-right badge" {% if counter.likeCount > 0 %} data-toggle="tooltip" data-placement="top" title="{{ counter.likersString }}" {% endif %}>
117
+										<span class="glyphicon glyphicon-ok"></span>&emsp;{{ counter.likeCount }}
118
+									</span>
119
+								{% elif counter.id == myCounter.id or counter.lastReset.who.id == myCounter.id %}
120
+			                		<span class="pull-right badge" {% if counter.likeCount > 0 %} data-toggle="tooltip" data-placement="top" title="{{ counter.likersString }}" {% endif %}>
121
+			                    		<span class="glyphicon glyphicon-heart"></span>&emsp;{{ counter.likeCount }}
122
+			                		</span>
123
+								{% else %}
124
+									<a class="pull-right badge" onclick="document.forms['like{{counter.id}}'].submit();">
125
+										<span class="glyphicon glyphicon-heart"></span>&emsp;{{ counter.likeCount }}
126
+									</a>
127
+								{% endif %}
115 128
 							{% endif %}
116 129
 						</h2>
117 130
 					</form>
118 131
 				</div>
119 132
 				<div class="seum-counter panel-body">
120 133
 					{% if counter.lastReset.noSeum %}
121
-					<strong>N'a pas encore eu le seum.</strong>
122
-					<br> {% else %}
123
-					<strong>
124
-					{% if counter.lastReset.selfSeum %}
125
-						A eu le seum il y a {{ counter.lastReset.formatted_delta }}.
134
+						<strong>{% trans "Has not got the seum yet" %}</strong><br />
126 135
 					{% else %}
127
-						{{ counter.lastReset.who.trigramme }} lui a foutu le seum il y a {{ counter.lastReset.formatted_delta }}.
136
+						<strong>
137
+							{% if counter.lastReset.selfSeum %}
138
+								{% trans "Has got the seum" %} {{ counter.lastReset.formatted_delta }}.
139
+							{% else %}
140
+								{{ counter.lastReset.who.trigramme }} {% trans "threw him/her the seum" %} {{ counter.lastReset.formatted_delta }}.
141
+							{% endif %}
142
+						</strong><br />
128 143
 					{% endif %}
129
-					</strong>
130
-					<br> {% endif %}
131
-
132 144
 					<p>{{ counter.lastReset.reason | hashtag }}</p>
133 145
 				</div>
134 146
 			</div>
135 147
 		</div>
136 148
 		{% endfor %}
137 149
 	</div>
150
+	{# Graphs #}
138 151
 	<div class="row">
139 152
 		<div class="col-sm-12">
140 153
 			<div class="panel panel-info">
141 154
 				<div class="panel-heading">
142
-					<h2 class="panel-title">Timeline des 24 heures du seum</h2>
155
+					<h2 class="panel-title">{% trans "Timeline of the 24h of the seum" %}{# Timeline des 24 heures du seum #}</h2>
143 156
 				</div>
144 157
 				<div class="panel-body timeline graphs">
145 158
 					{% if noTimeline %}
146
-					<div class="text-center text-muted">
147
-						<p>Pas de seum dans les dernières 24 heures...</p>
148
-					</div>
149
-					{% else %} {{ line_chart.as_html }} {% endif %}
159
+						<div class="text-center text-muted">
160
+							<p>{% trans "No seum in the last past 24h..." %}</p>
161
+						</div>
162
+					{% else %}
163
+						{{ line_chart.as_html }}
164
+					{% endif %}
150 165
 				</div>
151 166
 			</div>
152 167
 		</div>
@@ -155,14 +170,16 @@
155 170
 		<div class="col-sm-12">
156 171
 			<div class="panel panel-info">
157 172
 				<div class="panel-heading">
158
-					<h2 class="panel-title">Meilleurs seumeurs</h2>
173
+					<h2 class="panel-title">{% trans "Best seumers" %}</h2>
159 174
 				</div>
160 175
 				<div class="panel-body graphs">
161 176
 					{% if noBestSeum %}
162
-					<div class="text-center text-muted">
163
-						<p>Personne n'a eu le seum...</p>
164
-					</div>
165
-					{% else %} {{ best_chart.as_html }} {% endif %}
177
+						<div class="text-center text-muted">
178
+							<p>{% trans "Nobody has got the seum..." %}</p>
179
+						</div>
180
+					{% else %}
181
+						{{ best_chart.as_html }}
182
+					{% endif %}
166 183
 				</div>
167 184
 			</div>
168 185
 		</div>
@@ -171,14 +188,16 @@
171 188
 		<div class="col-sm-12">
172 189
 			<div class="panel panel-info">
173 190
 				<div class="panel-heading">
174
-					<h2 class="panel-title">Seumeurs les plus likés</h2>
191
+					<h2 class="panel-title">{% trans "Most liked seumers" %}</h2>
175 192
 				</div>
176 193
 				<div class="panel-body graphs">
177 194
 					{% if noBestLikees %}
178
-					<div class="text-center text-muted">
179
-						<p>Personne n'a liké...</p>
180
-					</div>
181
-					{% else %} {{ likees_chart.as_html }} {% endif %}
195
+						<div class="text-center text-muted">
196
+							<p>{% trans "Nobody liked..." %}</p>
197
+						</div>
198
+					{% else %}
199
+						{{ likees_chart.as_html }}
200
+					{% endif %}
182 201
 				</div>
183 202
 			</div>
184 203
 		</div>
@@ -187,14 +206,16 @@
187 206
 		<div class="col-sm-12">
188 207
 			<div class="panel panel-info">
189 208
 				<div class="panel-heading">
190
-					<h2 class="panel-title">Hashtags les plus populaires</h2>
209
+					<h2 class="panel-title">{% trans "Most popular hashtags" %}</h2>
191 210
 				</div>
192 211
 				<div class="panel-body graphs">
193 212
 					{% if noBestHashtags %}
194
-					<div class="text-center text-muted">
195
-						<p>Personne n'a utilisé de hashtags...</p>
196
-					</div>
197
-					{% else %} {{ hashtags_chart.as_html }} {% endif %}
213
+						<div class="text-center text-muted">
214
+							<p>{% trans "Nobody used any hashtag..." %}</p>
215
+						</div>
216
+					{% else %}
217
+						{{ hashtags_chart.as_html }}
218
+					{% endif %}
198 219
 				</div>
199 220
 			</div>
200 221
 		</div>
@@ -203,14 +224,16 @@
203 224
 		<div class="col-sm-12">
204 225
 			<div class="panel panel-info">
205 226
 				<div class="panel-heading">
206
-					<h2 class="panel-title">Meilleurs likeurs de seum</h2>
227
+					<h2 class="panel-title">{% trans "Best likers of seum" %}</h2>
207 228
 				</div>
208 229
 				<div class="panel-body graphs">
209 230
 					{% if noBestLikers %}
210
-					<div class="text-center text-muted">
211
-						<p>Personne n'a liké...</p>
212
-					</div>
213
-					{% else %} {{ likers_chart.as_html }} {% endif %}
231
+						<div class="text-center text-muted">
232
+							<p>{% trans "Nobody liked..." %}</p>
233
+						</div>
234
+					{% else %}
235
+						{{ likers_chart.as_html }}
236
+					{% endif %}
214 237
 				</div>
215 238
 			</div>
216 239
 		</div>
@@ -219,34 +242,36 @@
219 242
 		<div class="col-sm-12">
220 243
 			<div class="panel panel-info">
221 244
 				<div class="panel-heading">
222
-					<h2 class="panel-title">Activité seumesque</h2>
245
+					<h2 class="panel-title">{% trans "Seum activity" %}</h2>
223 246
 				</div>
224 247
 				<div class="panel-body graphs">
225 248
 					{% if noSeumActivity %}
226
-					<div class="text-center text-muted">
227
-						<p>Personne n'a eu le seum...</p>
228
-					</div>
229
-					{% else %} {{ activity_chart.as_html }} {% endif %}
249
+						<div class="text-center text-muted">
250
+							<p>{% trans "Nobody has got the seum..." %}</p>
251
+						</div>
252
+					{% else %}
253
+						{{ activity_chart.as_html }}
254
+					{% endif %}
230 255
 				</div>
231 256
 			</div>
232 257
 		</div>
233 258
 	</div>
234 259
 </div>
235 260
 <div class="row text-center">
236
-	<a href="{% url 'logout' %}" class="btn btn-danger">Se déconnecter</a>
237
-	<a href="{% url 'password_change' %}" class="btn btn-warning">Changer de mot de passe</a>
261
+	<a href="{% url 'logout' %}" class="btn btn-danger">{% trans "Logout" %}</a>
262
+	<a href="{% url 'password_change' %}" class="btn btn-warning">{% trans "Change password" %}</a>
238 263
 	<a href="{% url 'toggle_email_notifications' %}" class="btn btn-info">
239 264
 		{% if myCounter.email_notifications %}
240
-		Désactiver les notifications par mail
265
+			{% trans "Deactivate email notifications" %}
241 266
 		{% else %}
242
-		Activer les notifications par mail
267
+			{% trans "Activate email notifications" %}
243 268
 		{% endif %}
244 269
 	</a>
245 270
 	<a href="{% url 'toggle_sort_score' %}" class="btn btn-success">
246 271
 		{% if myCounter.sort_by_score %}
247
-		Trier les seums par ancienneté
272
+			{% trans "Sort seums by date" %}
248 273
 		{% else %}
249
-		Trier les seums par score
274
+			{% trans "Sort seums by score" %}
250 275
 		{% endif %}
251 276
 	</a>
252 277
 </div>

+ 108 - 86
counter/views/home.py

@@ -3,6 +3,7 @@ from datetime import datetime, timedelta
3 3
 import functools
4 4
 import math
5 5
 import random
6
+from time import clock
6 7
 
7 8
 from django import forms
8 9
 from django.contrib.auth.decorators import login_required
@@ -22,6 +23,8 @@ from babel.dates import format_timedelta, format_datetime
22 23
 from graphos.renderers import gchart
23 24
 from graphos.sources.model import ModelDataSource
24 25
 from graphos.sources.simple import SimpleDataSource
26
+import numpy as np
27
+import pandas as pd
25 28
 
26 29
 from counter.models import *
27 30
 from counter.utils import parseSeumReason
@@ -46,6 +49,7 @@ def index(request):
46 49
             # This person never had the seum
47 50
             myCounter.lastReset = Reset()
48 51
             myCounter.lastReset.delta = no_seum_delta
52
+            myCounter.lastReset.formatted_delta = format_timedelta(myCounter.lastReset.delta, locale=get_language(), threshold=1)
49 53
             myCounter.lastReset.noSeum = True
50 54
         else:
51 55
             myCounter.lastReset = myLastReset
@@ -58,8 +62,7 @@ def index(request):
58 62
             myCounter.likeCount = len(likesMe)
59 63
             if myCounter.likeCount > 0:
60 64
                 myCounter.likersString = ", ".join([like.liker.trigramme for like in likesMe])
61
-
62
-        myCounter.lastReset.formatted_delta = arrow.Arrow.fromdatetime(myCounter.lastReset.timestamp).humanize(locale=get_language())
65
+            myCounter.lastReset.formatted_delta = arrow.Arrow.fromdatetime(myCounter.lastReset.timestamp).humanize(locale=get_language())
63 66
 
64 67
     except Counter.DoesNotExist:
65 68
         return HttpResponseRedirect(reverse('login'))
@@ -79,6 +82,7 @@ def index(request):
79 82
         if len(lastReset) == 0:  # This person never had the seum
80 83
             counter.lastReset = Reset()
81 84
             counter.lastReset.delta = no_seum_delta
85
+            counter.lastReset.formatted_delta = format_timedelta(counter.lastReset.delta, locale=get_language(), threshold=1)
82 86
             counter.lastReset.noSeum = True
83 87
             counter.lastReset.likes_count = -1
84 88
             counter.CSSclass = "warning"
@@ -91,8 +95,7 @@ def index(request):
91 95
                 counter.lastReset.selfSeum = False
92 96
             # Now we compute the duration since the reset
93 97
             counter.lastReset.noSeum = False
94
-            counter.lastReset.delta = datetime.now(
95
-            ) - counter.lastReset.timestamp.replace(tzinfo=None)
98
+            counter.lastReset.delta = datetime.now() - counter.lastReset.timestamp.replace(tzinfo=None)
96 99
             # Defining CSS attributes for the counter
97 100
             counter.CSSclass = 'primary' if counter == myCounter else 'default'
98 101
             # Computing the total number of likes for this counter
@@ -101,9 +104,9 @@ def index(request):
101 104
             counter.alreadyLiked = myCounter in likesMe
102 105
             if counter.lastReset.likes_count > 0:
103 106
                 counter.likersString = ", ".join([like.liker.trigramme for like in likesMe])
107
+            counter.lastReset.formatted_delta = arrow.Arrow.fromdatetime(counter.lastReset.timestamp).humanize(locale=get_language())
104 108
 
105
-        counter.lastReset.formatted_delta = format_timedelta(
106
-            counter.lastReset.delta, locale='fr', threshold=1)
109
+        counter.likeCount = counter.lastReset.likes_count
107 110
         counter.isHidden = 'hidden'
108 111
 
109 112
     if myCounter.sort_by_score:
@@ -116,32 +119,60 @@ def index(request):
116 119
     else:
117 120
         counters = sorted(counters, key=lambda t: + t.lastReset.delta.total_seconds())
118 121
 
122
+
123
+    # ### GRAPHS ###
124
+    resets_raw = list(Reset.objects.select_related('who', 'counter').annotate(likes_count=Count('likes')))
125
+    likes_raw = list(Like.objects.select_related('liker', 'reset__counter').all())
126
+    hashtags_raw = list(Hashtag.objects.select_related('keyword').all())
127
+    # Prepare pandas.DataFrames to efficiently process the data
128
+    # About the counters
129
+    resets_cols = ['date', 'counter', 'counter_trigram', 'who', 'who_trigram', 'reason', 'likes_count']
130
+    resets_data = [[r.timestamp, r.counter.id, r.counter.trigramme, r.who, r.who, r.reason, r.likes_count] for r in resets_raw]
131
+    for r in resets_data:
132
+        r[3] = 0 if r[3] is None else r[3].id
133
+        r[4] = '' if r[4] is None else r[4].trigramme
134
+    resets_df = pd.DataFrame(resets_data, columns=resets_cols)
135
+    resets_df['timestamp'] = resets_df.date.map(lambda d: d.timestamp())
136
+    resets_df['self_seum'] = (resets_df.who.eq(np.zeros(resets_df.shape[0])) | resets_df.who.eq(resets_df.counter)).map(float)
137
+    resets_df['formatted_delta'] = resets_df.date.map(lambda d: arrow.Arrow.fromdatetime(d).humanize(locale=get_language()))
138
+    # About the likes
139
+    likes_cols = ['liker', 'liker_trigram', 'counter', 'counter_trigram']
140
+    likes_data = [[l.liker.id, l.liker.trigramme, l.reset.counter.id, l.reset.counter.trigramme] for l in likes_raw]
141
+    likes_df = pd.DataFrame(likes_data, columns=likes_cols)
142
+    # About the hashtags
143
+    hashtags_cols = ['keyword']
144
+    hashtags_data = [[h.keyword.text] for h in hashtags_raw]
145
+    hashtags_df = pd.DataFrame(hashtags_data, columns=hashtags_cols)
146
+
147
+
119 148
     # Timeline graph
120
-    resets = Reset.objects.select_related('who', 'counter').filter(timestamp__gte=timezone.now() - timedelta(days=1))
121
-    if resets.count() == 0:
149
+    timeline_resets = resets_df[resets_df.date > (datetime.now() - timedelta(days=1))].copy().reset_index()
150
+    if timeline_resets.shape[0] == 0:
122 151
         noTimeline = True
123 152
         line_chart = None
124 153
     else:
125 154
         noTimeline = False
126
-        for reset in resets:
127
-            reset.timestamp = {
128
-                'v': reset.timestamp.timestamp(),
129
-                'f': "Il y a " + format_timedelta(datetime.now() -
130
-                                                  reset.timestamp.replace(
131
-                                                      tzinfo=None),
132
-                                                  locale='fr', threshold=1)
133
-            }
134
-            if (reset.who is None or
135
-                    reset.who.id == reset.counter.id):
136
-                reset.Seum = {'v': 0,
137
-                              'f': reset.counter.trigramme +
138
-                              " : " + reset.reason}
155
+
156
+        # Construct legend for timeline dots
157
+        legend_ = np.zeros(timeline_resets.shape[0], dtype=np.object)
158
+        for i in range(timeline_resets.shape[0]):
159
+            row = timeline_resets.iloc[i]
160
+            if row['self_seum'] == 1:
161
+                legend_[i] = _('%(counter)s: %(reason)s') % {'counter': row['counter_trigram'], 'reason': row['reason']}
139 162
             else:
140
-                reset.Seum = {'v': 0,
141
-                              'f': reset.who.trigramme + ' à ' +
142
-                              reset.counter.trigramme +
143
-                              " : " + reset.reason}
144
-        line_data = ModelDataSource(resets, fields=['timestamp', 'Seum'])
163
+                legend_[i] = _('%(who)s to %(counter)s: %(reason)s') % {'who': row['who_trigram'], 'counter': row['counter_trigram'], 'reason': row['reason']}
164
+        timeline_resets['legend'] = legend_
165
+
166
+        # Generate graph
167
+        resets_ = [['', _('Seum')]]
168
+        for i in range(timeline_resets.shape[0]):
169
+            r = timeline_resets.iloc[i]
170
+            resets_.append([{'v': r.timestamp, 'f': r.formatted_delta}, {'v': 0, 'f': r.legend}])
171
+            # resets_.append({
172
+            #     'timestamp': {'v': r.date.timestamp(), 'f': r.formatted_delta},
173
+            #     'Seum': {'v': 0, 'f': r.legend},
174
+            # })
175
+        line_data = SimpleDataSource(resets_)
145 176
         line_chart = gchart.LineChart(line_data, options={
146 177
             'lineWidth': 0,
147 178
             'pointSize': 10,
@@ -150,8 +181,8 @@ def index(request):
150 181
             'hAxis': {
151 182
                 'ticks': [
152 183
                     {'v': (datetime.now() - timedelta(days=1)
153
-                           ).timestamp(), 'f': 'Il y a 24 h'},
154
-                    {'v': datetime.now().timestamp(), 'f': 'Présent'}
184
+                           ).timestamp(), 'f': _('24h ago')},
185
+                    {'v': datetime.now().timestamp(), 'f': _('Now')}
155 186
                 ]
156 187
             },
157 188
             'legend': 'none',
@@ -159,114 +190,105 @@ def index(request):
159 190
         })
160 191
 
161 192
     # Graph of greatest seumers
162
-    seumCounts = []
163
-    for counter in counters:
164
-        seumCounts.append([counter.trigramme, Reset.objects.filter(
165
-            counter=counter).count()])
166
-    if (len(seumCounts) == 0):
193
+    seum_counts_df = resets_df[['counter_trigram', 'self_seum']].copy()
194
+    seum_counts_df['seum_count'] = np.ones(seum_counts_df.shape[0], dtype=np.float32)
195
+    seum_counts_df = seum_counts_df.groupby(['counter_trigram']).sum().reset_index()
196
+    # TODO: Add the ratio self_seum / seum_count
197
+    if (seum_counts_df.shape[0] == 0):
167 198
         noBestSeum = True
168 199
         best_chart = None
169 200
     else:
170
-        seumCounts.sort(key=lambda x: -x[1])
171 201
         noBestSeum = False
172
-        seumCounts.insert(0, ['Trigramme', 'Nombre de seums'])
173
-        best_data = SimpleDataSource(seumCounts[:bestSeumeursNumber])
202
+        seum_counts_data = seum_counts_df.sort_values(by='seum_count', ascending=False)[['counter_trigram', 'seum_count']].values.tolist()
203
+        seum_counts_data.insert(0, [_('Trigram'), _('Number of seums')])
204
+        best_data = SimpleDataSource(seum_counts_data[:bestSeumeursNumber])
174 205
         best_chart = gchart.ColumnChart(best_data, options={
175 206
             'title': '',
176 207
             'legend': 'none',
177
-            'vAxis': {'title': 'Nombre de seums'},
178
-            'hAxis': {'title': 'Trigramme'},
208
+            'vAxis': {'title': _('Number of seums')},
209
+            'hAxis': {'title': _('Trigram')},
179 210
         })
180 211
 
181 212
     # Graph of seum activity
182
-    resets = Reset.objects.filter(
183
-        timestamp__gte=timezone.now() - timedelta(days=365))
184
-    months = {}
185
-    for reset in resets:
186
-        monthDate = datetime(reset.timestamp.year, reset.timestamp.month, 1)
187
-        months[monthDate] = months.get(monthDate, 0) + 1
188
-
189
-    monthList = sorted(months.items(), key=lambda t: t[0])
190
-    seumActivity = []
191
-    for month in monthList:
192
-        seumActivity.append(
193
-            [format_datetime(month[0], locale='fr',
194
-                             format="MMM Y").capitalize(), month[1]])
195
-    if (len(seumActivity) == 0):
213
+    resets_act = resets_df[resets_df.date > (timezone.now() - timedelta(days=365))][['date']].copy()
214
+    resets_act['year'] = resets_df.date.map(lambda d: d.year)
215
+    resets_act['month'] = resets_df.date.map(lambda d: d.month)
216
+    resets_act = resets_act.drop(['date'], axis=1)
217
+    resets_act['month_counts'] = np.ones(resets_act.shape[0], dtype=int)
218
+    resets_act = resets_act.groupby(['year', 'month']).sum().reset_index()
219
+    if resets_act.shape[0] == 0:
196 220
         noSeumActivity = True
197 221
         activity_chart = None
198 222
     else:
199 223
         noSeumActivity = False
200
-        seumActivity.insert(0, ['Mois', 'Nombre de seums'])
224
+        seumActivity = [
225
+            [arrow.Arrow(a[0], a[1], 1).format("MMM YYYY", locale=get_language()).capitalize(), a[2]]
226
+            for a in resets_act.values.tolist()
227
+        ]
228
+        seumActivity.insert(0, [_('Month'), _('Number of seums')])
201 229
         activity_data = SimpleDataSource(seumActivity)
202 230
         activity_chart = gchart.ColumnChart(activity_data, options={
203 231
             'title': '',
204 232
             'legend': 'none',
205
-            'vAxis': {'title': 'Nombre de seums'},
206
-            'hAxis': {'title': 'Mois'},
233
+            'vAxis': {'title': _('Number of seums')},
234
+            'hAxis': {'title': _('Month')},
207 235
         })
208 236
 
209 237
     # Graph of best likers
210
-    likersCounts = []
211
-    for counter in counters:
212
-        likersCounts.append(
213
-            [counter.trigramme, Like.objects.filter(liker=counter).count()])
214
-    if (len(likersCounts) == 0):
238
+    best_likers_df = likes_df.drop(['liker', 'counter', 'counter_trigram'], axis=1)
239
+    best_likers_df['count'] = np.ones(best_likers_df.shape[0], dtype=int)
240
+    best_likers_df = best_likers_df.groupby(['liker_trigram']).sum().reset_index()
241
+    if best_likers_df.shape[0] == 0:
215 242
         noBestLikers = True
216 243
         likers_chart = None
217 244
     else:
218
-        likersCounts.sort(key=lambda x: -x[1])
219 245
         noBestLikers = False
220
-        likersCounts.insert(0, ['Trigramme', 'Nombre de likes distribués'])
246
+        likersCounts = best_likers_df.sort_values(by='count', ascending=False).values.tolist()
247
+        likersCounts.insert(0, [_('Trigram'), _('Number of given likes')])
221 248
         likers_data = SimpleDataSource(likersCounts[:bestSeumeursNumber])
222 249
         likers_chart = gchart.ColumnChart(likers_data, options={
223 250
             'title': '',
224 251
             'legend': 'none',
225
-            'vAxis': {'title': 'Nombre de likes distribués'},
226
-            'hAxis': {'title': 'Trigramme'},
252
+            'vAxis': {'title': _('Number of given likes')},
253
+            'hAxis': {'title': _('Trigram')},
227 254
         })
228 255
 
229 256
     # Graph of popular hashtags
230
-    hashtagsCounts = []
231
-    keywords = Keyword.objects.all()
232
-    for keyword in keywords:
233
-        hashtagsCounts.append(
234
-            ['#' + keyword.text,
235
-             Hashtag.objects.filter(keyword=keyword).count()])
236
-    if (len(hashtagsCounts) == 0):
257
+    hashtags_df['count'] = np.ones(hashtags_df.shape[0], dtype=int)
258
+    hashtags_df = hashtags_df.groupby(['keyword']).sum().reset_index()
259
+    hashtags_df['keyword'] = hashtags_df.keyword.map(lambda x: '#' + x)
260
+    if hashtags_df.shape[0] == 0:
237 261
         noBestHashtags = True
238 262
         hashtags_chart = None
239 263
     else:
240
-        hashtagsCounts.sort(key=lambda x: -x[1])
241 264
         noBestHashtags = False
242
-        hashtagsCounts.insert(0, ['Trigramme', 'Nombre de likes distribués'])
243
-        hashtags_data = SimpleDataSource(hashtagsCounts[:bestSeumeursNumber])
265
+        hashtags_data = hashtags_df.sort_values(by='count', ascending=False).values.tolist()
266
+        hashtags_data.insert(0, [_('Hashtag'), _('Number of seums containing the hashtag')])
267
+        hashtags_data = SimpleDataSource(hashtags_data[:bestSeumeursNumber])
244 268
         hashtags_chart = gchart.ColumnChart(hashtags_data, options={
245 269
             'title': '',
246 270
             'legend': 'none',
247
-            'vAxis': {'title': 'Nombre de seums contenant le hashtag'},
248
-            'hAxis': {'title': 'Hashtag'},
271
+            'vAxis': {'title': _('Number of seums containing the hashtag')},
272
+            'hAxis': {'title': _('Hashtag')},
249 273
         })
250 274
 
251 275
     # Graph of best likee
252
-    likeesCounts = []
253
-    for counter in counters:
254
-        likeesCounts.append(
255
-            [counter.trigramme,
256
-             Like.objects.filter(reset__counter=counter).count()])
257
-    if (len(likeesCounts) == 0):
276
+    best_likees_df = likes_df.drop(['counter', 'liker', 'liker_trigram'], axis=1)
277
+    best_likees_df['count'] = np.ones(best_likees_df.shape[0], dtype=int)
278
+    best_likees_df = best_likees_df.groupby(['counter_trigram']).sum().reset_index()
279
+    if best_likees_df.shape[0] == 0:
258 280
         noBestLikees = True
259 281
         likees_chart = None
260 282
     else:
261
-        likeesCounts.sort(key=lambda x: -x[1])
262 283
         noBestLikees = False
263
-        likeesCounts.insert(0, ['Trigramme', 'Nombre de likes reçus'])
284
+        likeesCounts = best_likees_df.sort_values(by='count', ascending=False).values.tolist()
285
+        likeesCounts.insert(0, [_('Trigram'), _('Number of received likes')])
264 286
         likees_data = SimpleDataSource(likeesCounts[:bestSeumeursNumber])
265 287
         likees_chart = gchart.ColumnChart(likees_data, options={
266 288
             'title': '',
267 289
             'legend': 'none',
268
-            'vAxis': {'title': 'Nombre de likes reçus'},
269
-            'hAxis': {'title': 'Trigramme'},
290
+            'vAxis': {'title': _('Number of received likes')},
291
+            'hAxis': {'title': _('Trigram')},
270 292
         })
271 293
 
272 294
     # At last we render the page

+ 146 - 3
locale/en/LC_MESSAGES/django.po

@@ -8,7 +8,7 @@ msgid ""
8 8
 msgstr ""
9 9
 "Project-Id-Version: PACKAGE VERSION\n"
10 10
 "Report-Msgid-Bugs-To: \n"
11
-"POT-Creation-Date: 2017-01-21 18:15+0100\n"
11
+"POT-Creation-Date: 2017-01-22 14:00+0100\n"
12 12
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 13
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 14
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -68,7 +68,7 @@ msgstr ""
68 68
 
69 69
 #: counter/models.py:34
70 70
 #, python-format
71
-msgid "%(counter)s : %(datetime)s (%(reason)s)"
71
+msgid "%(counter)s: %(datetime)s (%(reason)s)"
72 72
 msgstr ""
73 73
 
74 74
 #: counter/models.py:48 counter/models.py:79
@@ -122,18 +122,23 @@ msgid "%(keyword)s for %(who)s"
122 122
 msgstr ""
123 123
 
124 124
 #: counter/templates/counterTemplate.html:47
125
+#: counter/templates/homeTemplate.html:32
126
+#: counter/templates/homeTemplate.html:134
125 127
 msgid "Has not got the seum yet"
126 128
 msgstr ""
127 129
 
128 130
 #: counter/templates/counterTemplate.html:51
131
+#: counter/templates/homeTemplate.html:138
129 132
 msgid "Has got the seum"
130 133
 msgstr ""
131 134
 
132 135
 #: counter/templates/counterTemplate.html:53
136
+#: counter/templates/homeTemplate.html:140
133 137
 msgid "threw him/her the seum"
134 138
 msgstr ""
135 139
 
136 140
 #: counter/templates/counterTemplate.html:61
141
+#: counter/templates/homeTemplate.html:45
137 142
 msgid "Reset"
138 143
 msgstr ""
139 144
 
@@ -142,6 +147,7 @@ msgid "Motive for the seum:"
142 147
 msgstr ""
143 148
 
144 149
 #: counter/templates/counterTemplate.html:74
150
+#: counter/templates/homeTemplate.html:89
145 151
 msgid "Throw the seum"
146 152
 msgstr ""
147 153
 
@@ -164,6 +170,7 @@ msgstr ""
164 170
 
165 171
 #: counter/templates/counterTemplate.html:111
166 172
 #: counter/templates/hashtagTemplate.html:27
173
+#: counter/templates/homeTemplate.html:82
167 174
 msgid "Motive"
168 175
 msgstr ""
169 176
 
@@ -213,7 +220,10 @@ msgid ""
213 220
 "seum identity!"
214 221
 msgstr ""
215 222
 
216
-#: counter/templates/createUser.html:36
223
+#: counter/templates/createUser.html:36 counter/templates/homeTemplate.html:76
224
+#: counter/views/home.py:203 counter/views/home.py:209
225
+#: counter/views/home.py:247 counter/views/home.py:253
226
+#: counter/views/home.py:285 counter/views/home.py:291
217 227
 msgid "Trigram"
218 228
 msgstr ""
219 229
 
@@ -277,6 +287,92 @@ msgstr ""
277 287
 msgid "Victim"
278 288
 msgstr ""
279 289
 
290
+#: counter/templates/homeTemplate.html:6
291
+msgid "Counters"
292
+msgstr ""
293
+
294
+#: counter/templates/homeTemplate.html:36
295
+msgid "I got the seum"
296
+msgstr ""
297
+
298
+#: counter/templates/homeTemplate.html:52
299
+msgid "Motive of the seum"
300
+msgstr ""
301
+
302
+#: counter/templates/homeTemplate.html:59
303
+msgid "I've got the seum"
304
+msgstr ""
305
+
306
+#: counter/templates/homeTemplate.html:70
307
+msgid "Break the seum wall"
308
+msgstr ""
309
+
310
+#: counter/templates/homeTemplate.html:155
311
+msgid "Timeline of the 24h of the seum"
312
+msgstr ""
313
+
314
+#: counter/templates/homeTemplate.html:160
315
+msgid "No seum in the last past 24h..."
316
+msgstr ""
317
+
318
+#: counter/templates/homeTemplate.html:173
319
+msgid "Best seumers"
320
+msgstr ""
321
+
322
+#: counter/templates/homeTemplate.html:178
323
+#: counter/templates/homeTemplate.html:250
324
+msgid "Nobody has got the seum..."
325
+msgstr ""
326
+
327
+#: counter/templates/homeTemplate.html:191
328
+msgid "Most liked seumers"
329
+msgstr ""
330
+
331
+#: counter/templates/homeTemplate.html:196
332
+#: counter/templates/homeTemplate.html:232
333
+msgid "Nobody liked..."
334
+msgstr ""
335
+
336
+#: counter/templates/homeTemplate.html:209
337
+msgid "Most popular hashtags"
338
+msgstr ""
339
+
340
+#: counter/templates/homeTemplate.html:214
341
+msgid "Nobody used any hashtag..."
342
+msgstr ""
343
+
344
+#: counter/templates/homeTemplate.html:227
345
+msgid "Best likers of seum"
346
+msgstr ""
347
+
348
+#: counter/templates/homeTemplate.html:245
349
+msgid "Seum activity"
350
+msgstr ""
351
+
352
+#: counter/templates/homeTemplate.html:261
353
+msgid "Logout"
354
+msgstr ""
355
+
356
+#: counter/templates/homeTemplate.html:262
357
+msgid "Change password"
358
+msgstr ""
359
+
360
+#: counter/templates/homeTemplate.html:265
361
+msgid "Deactivate email notifications"
362
+msgstr ""
363
+
364
+#: counter/templates/homeTemplate.html:267
365
+msgid "Activate email notifications"
366
+msgstr ""
367
+
368
+#: counter/templates/homeTemplate.html:272
369
+msgid "Sort seums by date"
370
+msgstr ""
371
+
372
+#: counter/templates/homeTemplate.html:274
373
+msgid "Sort seums by score"
374
+msgstr ""
375
+
280 376
 #: counter/templates/login.html:5
281 377
 msgid "Login"
282 378
 msgstr ""
@@ -357,6 +453,53 @@ msgstr ""
357 453
 msgid "From %(who)s: %(reason)s"
358 454
 msgstr ""
359 455
 
456
+#: counter/views/home.py:161
457
+#, python-format
458
+msgid "%(counter)s: %(reason)s"
459
+msgstr ""
460
+
461
+#: counter/views/home.py:163
462
+#, python-format
463
+msgid "%(who)s to %(counter)s: %(reason)s"
464
+msgstr ""
465
+
466
+#: counter/views/home.py:167
467
+msgid "Seum"
468
+msgstr ""
469
+
470
+#: counter/views/home.py:184
471
+msgid "24h ago"
472
+msgstr ""
473
+
474
+#: counter/views/home.py:185
475
+msgid "Now"
476
+msgstr ""
477
+
478
+#: counter/views/home.py:203 counter/views/home.py:208
479
+#: counter/views/home.py:228 counter/views/home.py:233
480
+msgid "Number of seums"
481
+msgstr ""
482
+
483
+#: counter/views/home.py:228 counter/views/home.py:234
484
+msgid "Month"
485
+msgstr ""
486
+
487
+#: counter/views/home.py:247 counter/views/home.py:252
488
+msgid "Number of given likes"
489
+msgstr ""
490
+
491
+#: counter/views/home.py:266 counter/views/home.py:272
492
+msgid "Hashtag"
493
+msgstr ""
494
+
495
+#: counter/views/home.py:266 counter/views/home.py:271
496
+msgid "Number of seums containing the hashtag"
497
+msgstr ""
498
+
499
+#: counter/views/home.py:285 counter/views/home.py:290
500
+msgid "Number of received likes"
501
+msgstr ""
502
+
360 503
 #: counter/views/user.py:22
361 504
 msgid "Passwords do not match."
362 505
 msgstr ""

+ 149 - 21
locale/fr/LC_MESSAGES/django.po

@@ -8,7 +8,7 @@ msgid ""
8 8
 msgstr ""
9 9
 "Project-Id-Version: PACKAGE VERSION\n"
10 10
 "Report-Msgid-Bugs-To: \n"
11
-"POT-Creation-Date: 2017-01-21 18:15+0100\n"
11
+"POT-Creation-Date: 2017-01-22 14:00+0100\n"
12 12
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 13
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 14
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -60,20 +60,16 @@ msgid "reason"
60 60
 msgstr "raison"
61 61
 
62 62
 #: counter/models.py:29
63
-#, fuzzy
64
-#| msgid "Victim"
65 63
 msgid "victim"
66 64
 msgstr "victime"
67 65
 
68 66
 #: counter/models.py:30
69
-#, fuzzy
70
-#| msgid "Seum giver"
71 67
 msgid "seum giver"
72 68
 msgstr "fouteur de seum"
73 69
 
74 70
 #: counter/models.py:34
75 71
 #, python-format
76
-msgid "%(counter)s : %(datetime)s (%(reason)s)"
72
+msgid "%(counter)s: %(datetime)s (%(reason)s)"
77 73
 msgstr "%(counter)s : %(datetime)s (%(reason)s)"
78 74
 
79 75
 #: counter/models.py:48 counter/models.py:79
@@ -127,28 +123,32 @@ msgid "%(keyword)s for %(who)s"
127 123
 msgstr "%(keyword)s pour %(who)s"
128 124
 
129 125
 #: counter/templates/counterTemplate.html:47
126
+#: counter/templates/homeTemplate.html:32
127
+#: counter/templates/homeTemplate.html:134
130 128
 msgid "Has not got the seum yet"
131 129
 msgstr "N'a pas encore eu le seum"
132 130
 
133 131
 #: counter/templates/counterTemplate.html:51
132
+#: counter/templates/homeTemplate.html:138
134 133
 msgid "Has got the seum"
135 134
 msgstr "A eu le seum"
136 135
 
137 136
 #: counter/templates/counterTemplate.html:53
137
+#: counter/templates/homeTemplate.html:140
138 138
 msgid "threw him/her the seum"
139
-msgstr "a foutu le seum"
139
+msgstr "lui a foutu le seum"
140 140
 
141 141
 #: counter/templates/counterTemplate.html:61
142
-#, fuzzy
143
-#| msgid "reset"
142
+#: counter/templates/homeTemplate.html:45
144 143
 msgid "Reset"
145 144
 msgstr "Remise à zéro"
146 145
 
147 146
 #: counter/templates/counterTemplate.html:67
148 147
 msgid "Motive for the seum:"
149
-msgstr "Motif du seum"
148
+msgstr "Motif du seum :"
150 149
 
151 150
 #: counter/templates/counterTemplate.html:74
151
+#: counter/templates/homeTemplate.html:89
152 152
 msgid "Throw the seum"
153 153
 msgstr "Foutre le seum"
154 154
 
@@ -171,6 +171,7 @@ msgstr "Date"
171 171
 
172 172
 #: counter/templates/counterTemplate.html:111
173 173
 #: counter/templates/hashtagTemplate.html:27
174
+#: counter/templates/homeTemplate.html:82
174 175
 msgid "Motive"
175 176
 msgstr "Motif"
176 177
 
@@ -216,8 +217,6 @@ msgstr ""
216 217
 "mais jouissif, peut-être désactivé ou réactivé par la suite."
217 218
 
218 219
 #: counter/templates/createUser.html:31
219
-#, fuzzy
220
-#| msgid "email notifications"
221 220
 msgid "Email notifications"
222 221
 msgstr "Notifications par email"
223 222
 
@@ -229,9 +228,10 @@ msgstr ""
229 228
 "Les autres utilisateurs ne pourront voir que ton pseudo et ton trigramme, ce "
230 229
 "sera ton identité seumesque !"
231 230
 
232
-#: counter/templates/createUser.html:36
233
-#, fuzzy
234
-#| msgid "trigram"
231
+#: counter/templates/createUser.html:36 counter/templates/homeTemplate.html:76
232
+#: counter/views/home.py:203 counter/views/home.py:209
233
+#: counter/views/home.py:247 counter/views/home.py:253
234
+#: counter/views/home.py:285 counter/views/home.py:291
235 235
 msgid "Trigram"
236 236
 msgstr "Trigramme"
237 237
 
@@ -300,15 +300,99 @@ msgstr "Liste des seums contenant"
300 300
 msgid "Victim"
301 301
 msgstr "Victime"
302 302
 
303
+#: counter/templates/homeTemplate.html:6
304
+msgid "Counters"
305
+msgstr "Compteurs"
306
+
307
+#: counter/templates/homeTemplate.html:36
308
+msgid "I got the seum"
309
+msgstr "J'ai eu le seum"
310
+
311
+#: counter/templates/homeTemplate.html:52
312
+msgid "Motive of the seum"
313
+msgstr "Motif du seum"
314
+
315
+#: counter/templates/homeTemplate.html:59
316
+msgid "I've got the seum"
317
+msgstr "J'ai le seum"
318
+
319
+#: counter/templates/homeTemplate.html:70
320
+msgid "Break the seum wall"
321
+msgstr "Brise le mur du seum"
322
+
323
+#: counter/templates/homeTemplate.html:155
324
+msgid "Timeline of the 24h of the seum"
325
+msgstr "Timeline des 24 heures du seum"
326
+
327
+#: counter/templates/homeTemplate.html:160
328
+msgid "No seum in the last past 24h..."
329
+msgstr "Pas de seum durant les dernières 24h..."
330
+
331
+#: counter/templates/homeTemplate.html:173
332
+msgid "Best seumers"
333
+msgstr "Meilleurs seumers"
334
+
335
+#: counter/templates/homeTemplate.html:178
336
+#: counter/templates/homeTemplate.html:250
337
+msgid "Nobody has got the seum..."
338
+msgstr "Personne n'a eu le seum..."
339
+
340
+#: counter/templates/homeTemplate.html:191
341
+msgid "Most liked seumers"
342
+msgstr "Seumers les plus likés"
343
+
344
+#: counter/templates/homeTemplate.html:196
345
+#: counter/templates/homeTemplate.html:232
346
+msgid "Nobody liked..."
347
+msgstr "Personne n'a aimé..."
348
+
349
+#: counter/templates/homeTemplate.html:209
350
+msgid "Most popular hashtags"
351
+msgstr "Hashtags les plus populaires"
352
+
353
+#: counter/templates/homeTemplate.html:214
354
+msgid "Nobody used any hashtag..."
355
+msgstr "Personne n'a utilisé de hashtag..."
356
+
357
+#: counter/templates/homeTemplate.html:227
358
+msgid "Best likers of seum"
359
+msgstr "Meilleurs likeurs de seum"
360
+
361
+#: counter/templates/homeTemplate.html:245
362
+msgid "Seum activity"
363
+msgstr "Activité seumesque"
364
+
365
+#: counter/templates/homeTemplate.html:261
366
+msgid "Logout"
367
+msgstr "Se déconnecter"
368
+
369
+#: counter/templates/homeTemplate.html:262
370
+msgid "Change password"
371
+msgstr "Modifier le mot de passe"
372
+
373
+#: counter/templates/homeTemplate.html:265
374
+msgid "Deactivate email notifications"
375
+msgstr "Désactiver les notifications par email"
376
+
377
+#: counter/templates/homeTemplate.html:267
378
+msgid "Activate email notifications"
379
+msgstr "Activer les notifications par email"
380
+
381
+#: counter/templates/homeTemplate.html:272
382
+msgid "Sort seums by date"
383
+msgstr "Trier les seums par ancienneté"
384
+
385
+#: counter/templates/homeTemplate.html:274
386
+msgid "Sort seums by score"
387
+msgstr "Trier les seums par score"
388
+
303 389
 #: counter/templates/login.html:5
304 390
 msgid "Login"
305 391
 msgstr "Login"
306 392
 
307 393
 #: counter/templates/login.html:17
308
-#, fuzzy
309
-#| msgid "Login to access the website!"
310 394
 msgid "Login to get the seum!"
311
-msgstr "Connecte-toi pour accéder au site !"
395
+msgstr "Connecte-toi pour avoir le seum !"
312 396
 
313 397
 #: counter/templates/login.html:23
314 398
 msgid "Username"
@@ -386,6 +470,53 @@ msgstr "inconnu"
386 470
 msgid "From %(who)s: %(reason)s"
387 471
 msgstr "De %(who)s : %(reason)s"
388 472
 
473
+#: counter/views/home.py:161
474
+#, python-format
475
+msgid "%(counter)s: %(reason)s"
476
+msgstr "%(counter)s : %(reason)s"
477
+
478
+#: counter/views/home.py:163
479
+#, python-format
480
+msgid "%(who)s to %(counter)s: %(reason)s"
481
+msgstr "%(who)s pour %(counter)s : %(reason)s"
482
+
483
+#: counter/views/home.py:167
484
+msgid "Seum"
485
+msgstr "Seum"
486
+
487
+#: counter/views/home.py:184
488
+msgid "24h ago"
489
+msgstr "il y a 24h"
490
+
491
+#: counter/views/home.py:185
492
+msgid "Now"
493
+msgstr "Présent"
494
+
495
+#: counter/views/home.py:203 counter/views/home.py:208
496
+#: counter/views/home.py:228 counter/views/home.py:233
497
+msgid "Number of seums"
498
+msgstr "Nombre de seums"
499
+
500
+#: counter/views/home.py:228 counter/views/home.py:234
501
+msgid "Month"
502
+msgstr "Mois"
503
+
504
+#: counter/views/home.py:247 counter/views/home.py:252
505
+msgid "Number of given likes"
506
+msgstr "Nombre de likes distribués"
507
+
508
+#: counter/views/home.py:266 counter/views/home.py:272
509
+msgid "Hashtag"
510
+msgstr "Hashtag"
511
+
512
+#: counter/views/home.py:266 counter/views/home.py:271
513
+msgid "Number of seums containing the hashtag"
514
+msgstr "Nombre de seums contenant le hashtag"
515
+
516
+#: counter/views/home.py:285 counter/views/home.py:290
517
+msgid "Number of received likes"
518
+msgstr "Nombre de likes reçus"
519
+
389 520
 #: counter/views/user.py:22
390 521
 msgid "Passwords do not match."
391 522
 msgstr "Les mots de passe sont différents."
@@ -405,6 +536,3 @@ msgstr "Anglais"
405 536
 #: seum/settings.py:112
406 537
 msgid "French"
407 538
 msgstr "Français"
408
-
409
-#~ msgid "Seum"
410
-#~ msgstr "Seum"

+ 4 - 2
requirements.txt

@@ -1,5 +1,7 @@
1
-django==1.10
1
+arrow==0.10
2 2
 babel
3
+django==1.10
3 4
 django-bootstrap3
4
-django-graphos-3
5 5
 django-debug-toolbar==1.6
6
+django-graphos-3
7
+pandas==0.19