Просмотр исходного кода

Translate homepage. Optimise graphs generation.

Camille Masset лет назад: 9
Родитель
Сommit
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
 
31
 
32
     def __str__(self):
32
     def __str__(self):
33
         if self.who is None or self.who == self.counter:
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
                 'counter': self.counter,
35
                 'counter': self.counter,
36
                 'datetime': arrow.Arrow.fromdatetime(self.timestamp).humanize(locale=(get_language() or 'en')), # dirty hack...
36
                 'datetime': arrow.Arrow.fromdatetime(self.timestamp).humanize(locale=(get_language() or 'en')), # dirty hack...
37
                 'reason': self.reason
37
                 'reason': self.reason

+ 99 - 74
counter/templates/homeTemplate.html

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
 <div class="text-center">
9
 <div class="text-center">
3
 	<h1><a class="counter-link" href="{% url 'home' %}">SeumBook™</a></h1>
10
 	<h1><a class="counter-link" href="{% url 'home' %}">SeumBook™</a></h1>
4
 </div>
11
 </div>
5
 <div class="container-fluid">
12
 <div class="container-fluid">
6
 	<div class="row" id="my-counter">
13
 	<div class="row" id="my-counter">
14
+		{# my counter #}
7
 		<div class="col-sm-6">
15
 		<div class="col-sm-6">
8
 			<div class="panel panel-primary">
16
 			<div class="panel panel-primary">
9
 				<div class="panel-heading">
17
 				<div class="panel-heading">
21
 				<div class="primary-counter panel-body" id="container{{myCounter.id}}">
29
 				<div class="primary-counter panel-body" id="container{{myCounter.id}}">
22
 					<div style="width:100%;">
30
 					<div style="width:100%;">
23
 						{% if myCounter.lastReset.noSeum %}
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
 						{% else %}
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
 						{% endif %}
41
 						{% endif %}
32
-					</strong>
33
-						<br> {% endif %}
34
 						<p>{{ myCounter.lastReset.reason | hashtag }}</p>
42
 						<p>{{ myCounter.lastReset.reason | hashtag }}</p>
35
 						<div class="text-center" id="button{{myCounter.id}}">
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
 						</div>
47
 						</div>
48
+						{# reset form for my counter #}
38
 						<form style="display:none" id="counter{{myCounter.id}}" action="{% url 'reset-counter' %}" method="post">
49
 						<form style="display:none" id="counter{{myCounter.id}}" action="{% url 'reset-counter' %}" method="post">
39
 							{% csrf_token %}
50
 							{% csrf_token %}
40
 							<div class="form-group">
51
 							<div class="form-group">
41
-								<label for="reason">Motif du seum</label>
52
+								<label for="reason">{% trans "Motive of the seum" %}</label>
42
 								<input id="reason{{myCounter.id}}" type="text" class="form-control" name="reason"></input>
53
 								<input id="reason{{myCounter.id}}" type="text" class="form-control" name="reason"></input>
43
 							</div>
54
 							</div>
44
 							<input type="hidden" name="counter" value="{{myCounter.id}}"></input>
55
 							<input type="hidden" name="counter" value="{{myCounter.id}}"></input>
45
 							<input type="hidden" name="redirect" value="{% url 'home' %}"></input>
56
 							<input type="hidden" name="redirect" value="{% url 'home' %}"></input>
46
 							<input type="hidden" name="who" value="{{myCounter.id}}"></input>
57
 							<input type="hidden" name="who" value="{{myCounter.id}}"></input>
47
 							<div class="text-center">
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
 							</div>
60
 							</div>
50
 						</form>
61
 						</form>
51
 					</div>
62
 					</div>
52
 				</div>
63
 				</div>
53
 			</div>
64
 			</div>
54
 		</div>
65
 		</div>
66
+		{# QuickSeum #}
55
 		<div class="col-sm-6">
67
 		<div class="col-sm-6">
56
 			<div class="panel panel-primary">
68
 			<div class="panel panel-primary">
57
 				<div class="panel-heading">
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
 				</div>
71
 				</div>
60
 				<div class="primary-counter panel-body">
72
 				<div class="primary-counter panel-body">
61
 					<form class="form-horizontal" action="{% url 'reset-counter'%}" method="POST" style="width:100%;">
73
 					<form class="form-horizontal" action="{% url 'reset-counter'%}" method="POST" style="width:100%;">
62
 						{% csrf_token %}
74
 						{% csrf_token %}
63
 						<div class="form-group">
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
 							<div class="col-sm-9">
77
 							<div class="col-sm-9">
66
 								<input id="id_quicktrigramme" maxlength="3" type="text" class="form-control text-uppercase" name="trigramme" onkeyup="this.value=this.value.toUpperCase();" required />
78
 								<input id="id_quicktrigramme" maxlength="3" type="text" class="form-control text-uppercase" name="trigramme" onkeyup="this.value=this.value.toUpperCase();" required />
67
 							</div>
79
 							</div>
68
 						</div>
80
 						</div>
69
 						<div class="form-group">
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
 							<div class="col-sm-9">
83
 							<div class="col-sm-9">
72
 								<input type="text" class="form-control" id="id_quickreason" name="reason" />
84
 								<input type="text" class="form-control" id="id_quickreason" name="reason" />
73
 							</div>
85
 							</div>
74
 						</div>
86
 						</div>
75
 						<div class="form-group">
87
 						<div class="form-group">
76
 							<div class="col-sm-offset-3 col-sm-9">
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
 							</div>
90
 							</div>
79
 						</div>
91
 						</div>
80
 						<input type="hidden" name="who" value="{{myCounter.id}}"></input>
92
 						<input type="hidden" name="who" value="{{myCounter.id}}"></input>
84
 			</div>
96
 			</div>
85
 		</div>
97
 		</div>
86
 	</div>
98
 	</div>
99
+	{# Counters panel #}
87
 	<div class="row">
100
 	<div class="row">
88
 		{% for counter in counters %}
101
 		{% for counter in counters %}
89
 		<div class="col-md-4 col-sm-6 col-lg-3">
102
 		<div class="col-md-4 col-sm-6 col-lg-3">
99
 								<b>{{ counter.trigramme }}</b> <small>{{ counter.name }}</small>
112
 								<b>{{ counter.trigramme }}</b> <small>{{ counter.name }}</small>
100
 							</a>
113
 							</a>
101
 							{% if not counter.lastReset.noSeum %}
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
 							{% endif %}
128
 							{% endif %}
116
 						</h2>
129
 						</h2>
117
 					</form>
130
 					</form>
118
 				</div>
131
 				</div>
119
 				<div class="seum-counter panel-body">
132
 				<div class="seum-counter panel-body">
120
 					{% if counter.lastReset.noSeum %}
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
 					{% else %}
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
 					{% endif %}
143
 					{% endif %}
129
-					</strong>
130
-					<br> {% endif %}
131
-
132
 					<p>{{ counter.lastReset.reason | hashtag }}</p>
144
 					<p>{{ counter.lastReset.reason | hashtag }}</p>
133
 				</div>
145
 				</div>
134
 			</div>
146
 			</div>
135
 		</div>
147
 		</div>
136
 		{% endfor %}
148
 		{% endfor %}
137
 	</div>
149
 	</div>
150
+	{# Graphs #}
138
 	<div class="row">
151
 	<div class="row">
139
 		<div class="col-sm-12">
152
 		<div class="col-sm-12">
140
 			<div class="panel panel-info">
153
 			<div class="panel panel-info">
141
 				<div class="panel-heading">
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
 				</div>
156
 				</div>
144
 				<div class="panel-body timeline graphs">
157
 				<div class="panel-body timeline graphs">
145
 					{% if noTimeline %}
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
 				</div>
165
 				</div>
151
 			</div>
166
 			</div>
152
 		</div>
167
 		</div>
155
 		<div class="col-sm-12">
170
 		<div class="col-sm-12">
156
 			<div class="panel panel-info">
171
 			<div class="panel panel-info">
157
 				<div class="panel-heading">
172
 				<div class="panel-heading">
158
-					<h2 class="panel-title">Meilleurs seumeurs</h2>
173
+					<h2 class="panel-title">{% trans "Best seumers" %}</h2>
159
 				</div>
174
 				</div>
160
 				<div class="panel-body graphs">
175
 				<div class="panel-body graphs">
161
 					{% if noBestSeum %}
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
 				</div>
183
 				</div>
167
 			</div>
184
 			</div>
168
 		</div>
185
 		</div>
171
 		<div class="col-sm-12">
188
 		<div class="col-sm-12">
172
 			<div class="panel panel-info">
189
 			<div class="panel panel-info">
173
 				<div class="panel-heading">
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
 				</div>
192
 				</div>
176
 				<div class="panel-body graphs">
193
 				<div class="panel-body graphs">
177
 					{% if noBestLikees %}
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
 				</div>
201
 				</div>
183
 			</div>
202
 			</div>
184
 		</div>
203
 		</div>
187
 		<div class="col-sm-12">
206
 		<div class="col-sm-12">
188
 			<div class="panel panel-info">
207
 			<div class="panel panel-info">
189
 				<div class="panel-heading">
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
 				</div>
210
 				</div>
192
 				<div class="panel-body graphs">
211
 				<div class="panel-body graphs">
193
 					{% if noBestHashtags %}
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
 				</div>
219
 				</div>
199
 			</div>
220
 			</div>
200
 		</div>
221
 		</div>
203
 		<div class="col-sm-12">
224
 		<div class="col-sm-12">
204
 			<div class="panel panel-info">
225
 			<div class="panel panel-info">
205
 				<div class="panel-heading">
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
 				</div>
228
 				</div>
208
 				<div class="panel-body graphs">
229
 				<div class="panel-body graphs">
209
 					{% if noBestLikers %}
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
 				</div>
237
 				</div>
215
 			</div>
238
 			</div>
216
 		</div>
239
 		</div>
219
 		<div class="col-sm-12">
242
 		<div class="col-sm-12">
220
 			<div class="panel panel-info">
243
 			<div class="panel panel-info">
221
 				<div class="panel-heading">
244
 				<div class="panel-heading">
222
-					<h2 class="panel-title">Activité seumesque</h2>
245
+					<h2 class="panel-title">{% trans "Seum activity" %}</h2>
223
 				</div>
246
 				</div>
224
 				<div class="panel-body graphs">
247
 				<div class="panel-body graphs">
225
 					{% if noSeumActivity %}
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
 				</div>
255
 				</div>
231
 			</div>
256
 			</div>
232
 		</div>
257
 		</div>
233
 	</div>
258
 	</div>
234
 </div>
259
 </div>
235
 <div class="row text-center">
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
 	<a href="{% url 'toggle_email_notifications' %}" class="btn btn-info">
263
 	<a href="{% url 'toggle_email_notifications' %}" class="btn btn-info">
239
 		{% if myCounter.email_notifications %}
264
 		{% if myCounter.email_notifications %}
240
-		Désactiver les notifications par mail
265
+			{% trans "Deactivate email notifications" %}
241
 		{% else %}
266
 		{% else %}
242
-		Activer les notifications par mail
267
+			{% trans "Activate email notifications" %}
243
 		{% endif %}
268
 		{% endif %}
244
 	</a>
269
 	</a>
245
 	<a href="{% url 'toggle_sort_score' %}" class="btn btn-success">
270
 	<a href="{% url 'toggle_sort_score' %}" class="btn btn-success">
246
 		{% if myCounter.sort_by_score %}
271
 		{% if myCounter.sort_by_score %}
247
-		Trier les seums par ancienneté
272
+			{% trans "Sort seums by date" %}
248
 		{% else %}
273
 		{% else %}
249
-		Trier les seums par score
274
+			{% trans "Sort seums by score" %}
250
 		{% endif %}
275
 		{% endif %}
251
 	</a>
276
 	</a>
252
 </div>
277
 </div>

+ 108 - 86
counter/views/home.py

3
 import functools
3
 import functools
4
 import math
4
 import math
5
 import random
5
 import random
6
+from time import clock
6
 
7
 
7
 from django import forms
8
 from django import forms
8
 from django.contrib.auth.decorators import login_required
9
 from django.contrib.auth.decorators import login_required
22
 from graphos.renderers import gchart
23
 from graphos.renderers import gchart
23
 from graphos.sources.model import ModelDataSource
24
 from graphos.sources.model import ModelDataSource
24
 from graphos.sources.simple import SimpleDataSource
25
 from graphos.sources.simple import SimpleDataSource
26
+import numpy as np
27
+import pandas as pd
25
 
28
 
26
 from counter.models import *
29
 from counter.models import *
27
 from counter.utils import parseSeumReason
30
 from counter.utils import parseSeumReason
46
             # This person never had the seum
49
             # This person never had the seum
47
             myCounter.lastReset = Reset()
50
             myCounter.lastReset = Reset()
48
             myCounter.lastReset.delta = no_seum_delta
51
             myCounter.lastReset.delta = no_seum_delta
52
+            myCounter.lastReset.formatted_delta = format_timedelta(myCounter.lastReset.delta, locale=get_language(), threshold=1)
49
             myCounter.lastReset.noSeum = True
53
             myCounter.lastReset.noSeum = True
50
         else:
54
         else:
51
             myCounter.lastReset = myLastReset
55
             myCounter.lastReset = myLastReset
58
             myCounter.likeCount = len(likesMe)
62
             myCounter.likeCount = len(likesMe)
59
             if myCounter.likeCount > 0:
63
             if myCounter.likeCount > 0:
60
                 myCounter.likersString = ", ".join([like.liker.trigramme for like in likesMe])
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
     except Counter.DoesNotExist:
67
     except Counter.DoesNotExist:
65
         return HttpResponseRedirect(reverse('login'))
68
         return HttpResponseRedirect(reverse('login'))
79
         if len(lastReset) == 0:  # This person never had the seum
82
         if len(lastReset) == 0:  # This person never had the seum
80
             counter.lastReset = Reset()
83
             counter.lastReset = Reset()
81
             counter.lastReset.delta = no_seum_delta
84
             counter.lastReset.delta = no_seum_delta
85
+            counter.lastReset.formatted_delta = format_timedelta(counter.lastReset.delta, locale=get_language(), threshold=1)
82
             counter.lastReset.noSeum = True
86
             counter.lastReset.noSeum = True
83
             counter.lastReset.likes_count = -1
87
             counter.lastReset.likes_count = -1
84
             counter.CSSclass = "warning"
88
             counter.CSSclass = "warning"
91
                 counter.lastReset.selfSeum = False
95
                 counter.lastReset.selfSeum = False
92
             # Now we compute the duration since the reset
96
             # Now we compute the duration since the reset
93
             counter.lastReset.noSeum = False
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
             # Defining CSS attributes for the counter
99
             # Defining CSS attributes for the counter
97
             counter.CSSclass = 'primary' if counter == myCounter else 'default'
100
             counter.CSSclass = 'primary' if counter == myCounter else 'default'
98
             # Computing the total number of likes for this counter
101
             # Computing the total number of likes for this counter
101
             counter.alreadyLiked = myCounter in likesMe
104
             counter.alreadyLiked = myCounter in likesMe
102
             if counter.lastReset.likes_count > 0:
105
             if counter.lastReset.likes_count > 0:
103
                 counter.likersString = ", ".join([like.liker.trigramme for like in likesMe])
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
         counter.isHidden = 'hidden'
110
         counter.isHidden = 'hidden'
108
 
111
 
109
     if myCounter.sort_by_score:
112
     if myCounter.sort_by_score:
116
     else:
119
     else:
117
         counters = sorted(counters, key=lambda t: + t.lastReset.delta.total_seconds())
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
     # Timeline graph
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
         noTimeline = True
151
         noTimeline = True
123
         line_chart = None
152
         line_chart = None
124
     else:
153
     else:
125
         noTimeline = False
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
             else:
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
         line_chart = gchart.LineChart(line_data, options={
176
         line_chart = gchart.LineChart(line_data, options={
146
             'lineWidth': 0,
177
             'lineWidth': 0,
147
             'pointSize': 10,
178
             'pointSize': 10,
150
             'hAxis': {
181
             'hAxis': {
151
                 'ticks': [
182
                 'ticks': [
152
                     {'v': (datetime.now() - timedelta(days=1)
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
             'legend': 'none',
188
             'legend': 'none',
159
         })
190
         })
160
 
191
 
161
     # Graph of greatest seumers
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
         noBestSeum = True
198
         noBestSeum = True
168
         best_chart = None
199
         best_chart = None
169
     else:
200
     else:
170
-        seumCounts.sort(key=lambda x: -x[1])
171
         noBestSeum = False
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
         best_chart = gchart.ColumnChart(best_data, options={
205
         best_chart = gchart.ColumnChart(best_data, options={
175
             'title': '',
206
             'title': '',
176
             'legend': 'none',
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
     # Graph of seum activity
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
         noSeumActivity = True
220
         noSeumActivity = True
197
         activity_chart = None
221
         activity_chart = None
198
     else:
222
     else:
199
         noSeumActivity = False
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
         activity_data = SimpleDataSource(seumActivity)
229
         activity_data = SimpleDataSource(seumActivity)
202
         activity_chart = gchart.ColumnChart(activity_data, options={
230
         activity_chart = gchart.ColumnChart(activity_data, options={
203
             'title': '',
231
             'title': '',
204
             'legend': 'none',
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
     # Graph of best likers
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
         noBestLikers = True
242
         noBestLikers = True
216
         likers_chart = None
243
         likers_chart = None
217
     else:
244
     else:
218
-        likersCounts.sort(key=lambda x: -x[1])
219
         noBestLikers = False
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
         likers_data = SimpleDataSource(likersCounts[:bestSeumeursNumber])
248
         likers_data = SimpleDataSource(likersCounts[:bestSeumeursNumber])
222
         likers_chart = gchart.ColumnChart(likers_data, options={
249
         likers_chart = gchart.ColumnChart(likers_data, options={
223
             'title': '',
250
             'title': '',
224
             'legend': 'none',
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
     # Graph of popular hashtags
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
         noBestHashtags = True
261
         noBestHashtags = True
238
         hashtags_chart = None
262
         hashtags_chart = None
239
     else:
263
     else:
240
-        hashtagsCounts.sort(key=lambda x: -x[1])
241
         noBestHashtags = False
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
         hashtags_chart = gchart.ColumnChart(hashtags_data, options={
268
         hashtags_chart = gchart.ColumnChart(hashtags_data, options={
245
             'title': '',
269
             'title': '',
246
             'legend': 'none',
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
     # Graph of best likee
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
         noBestLikees = True
280
         noBestLikees = True
259
         likees_chart = None
281
         likees_chart = None
260
     else:
282
     else:
261
-        likeesCounts.sort(key=lambda x: -x[1])
262
         noBestLikees = False
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
         likees_data = SimpleDataSource(likeesCounts[:bestSeumeursNumber])
286
         likees_data = SimpleDataSource(likeesCounts[:bestSeumeursNumber])
265
         likees_chart = gchart.ColumnChart(likees_data, options={
287
         likees_chart = gchart.ColumnChart(likees_data, options={
266
             'title': '',
288
             'title': '',
267
             'legend': 'none',
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
     # At last we render the page
294
     # At last we render the page

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

8
 msgstr ""
8
 msgstr ""
9
 "Project-Id-Version: PACKAGE VERSION\n"
9
 "Project-Id-Version: PACKAGE VERSION\n"
10
 "Report-Msgid-Bugs-To: \n"
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
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
 "Language-Team: LANGUAGE <LL@li.org>\n"
14
 "Language-Team: LANGUAGE <LL@li.org>\n"
68
 
68
 
69
 #: counter/models.py:34
69
 #: counter/models.py:34
70
 #, python-format
70
 #, python-format
71
-msgid "%(counter)s : %(datetime)s (%(reason)s)"
71
+msgid "%(counter)s: %(datetime)s (%(reason)s)"
72
 msgstr ""
72
 msgstr ""
73
 
73
 
74
 #: counter/models.py:48 counter/models.py:79
74
 #: counter/models.py:48 counter/models.py:79
122
 msgstr ""
122
 msgstr ""
123
 
123
 
124
 #: counter/templates/counterTemplate.html:47
124
 #: counter/templates/counterTemplate.html:47
125
+#: counter/templates/homeTemplate.html:32
126
+#: counter/templates/homeTemplate.html:134
125
 msgid "Has not got the seum yet"
127
 msgid "Has not got the seum yet"
126
 msgstr ""
128
 msgstr ""
127
 
129
 
128
 #: counter/templates/counterTemplate.html:51
130
 #: counter/templates/counterTemplate.html:51
131
+#: counter/templates/homeTemplate.html:138
129
 msgid "Has got the seum"
132
 msgid "Has got the seum"
130
 msgstr ""
133
 msgstr ""
131
 
134
 
132
 #: counter/templates/counterTemplate.html:53
135
 #: counter/templates/counterTemplate.html:53
136
+#: counter/templates/homeTemplate.html:140
133
 msgid "threw him/her the seum"
137
 msgid "threw him/her the seum"
134
 msgstr ""
138
 msgstr ""
135
 
139
 
136
 #: counter/templates/counterTemplate.html:61
140
 #: counter/templates/counterTemplate.html:61
141
+#: counter/templates/homeTemplate.html:45
137
 msgid "Reset"
142
 msgid "Reset"
138
 msgstr ""
143
 msgstr ""
139
 
144
 
142
 msgstr ""
147
 msgstr ""
143
 
148
 
144
 #: counter/templates/counterTemplate.html:74
149
 #: counter/templates/counterTemplate.html:74
150
+#: counter/templates/homeTemplate.html:89
145
 msgid "Throw the seum"
151
 msgid "Throw the seum"
146
 msgstr ""
152
 msgstr ""
147
 
153
 
164
 
170
 
165
 #: counter/templates/counterTemplate.html:111
171
 #: counter/templates/counterTemplate.html:111
166
 #: counter/templates/hashtagTemplate.html:27
172
 #: counter/templates/hashtagTemplate.html:27
173
+#: counter/templates/homeTemplate.html:82
167
 msgid "Motive"
174
 msgid "Motive"
168
 msgstr ""
175
 msgstr ""
169
 
176
 
213
 "seum identity!"
220
 "seum identity!"
214
 msgstr ""
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
 msgid "Trigram"
227
 msgid "Trigram"
218
 msgstr ""
228
 msgstr ""
219
 
229
 
277
 msgid "Victim"
287
 msgid "Victim"
278
 msgstr ""
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
 #: counter/templates/login.html:5
376
 #: counter/templates/login.html:5
281
 msgid "Login"
377
 msgid "Login"
282
 msgstr ""
378
 msgstr ""
357
 msgid "From %(who)s: %(reason)s"
453
 msgid "From %(who)s: %(reason)s"
358
 msgstr ""
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
 #: counter/views/user.py:22
503
 #: counter/views/user.py:22
361
 msgid "Passwords do not match."
504
 msgid "Passwords do not match."
362
 msgstr ""
505
 msgstr ""

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

8
 msgstr ""
8
 msgstr ""
9
 "Project-Id-Version: PACKAGE VERSION\n"
9
 "Project-Id-Version: PACKAGE VERSION\n"
10
 "Report-Msgid-Bugs-To: \n"
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
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
 "Language-Team: LANGUAGE <LL@li.org>\n"
14
 "Language-Team: LANGUAGE <LL@li.org>\n"
60
 msgstr "raison"
60
 msgstr "raison"
61
 
61
 
62
 #: counter/models.py:29
62
 #: counter/models.py:29
63
-#, fuzzy
64
-#| msgid "Victim"
65
 msgid "victim"
63
 msgid "victim"
66
 msgstr "victime"
64
 msgstr "victime"
67
 
65
 
68
 #: counter/models.py:30
66
 #: counter/models.py:30
69
-#, fuzzy
70
-#| msgid "Seum giver"
71
 msgid "seum giver"
67
 msgid "seum giver"
72
 msgstr "fouteur de seum"
68
 msgstr "fouteur de seum"
73
 
69
 
74
 #: counter/models.py:34
70
 #: counter/models.py:34
75
 #, python-format
71
 #, python-format
76
-msgid "%(counter)s : %(datetime)s (%(reason)s)"
72
+msgid "%(counter)s: %(datetime)s (%(reason)s)"
77
 msgstr "%(counter)s : %(datetime)s (%(reason)s)"
73
 msgstr "%(counter)s : %(datetime)s (%(reason)s)"
78
 
74
 
79
 #: counter/models.py:48 counter/models.py:79
75
 #: counter/models.py:48 counter/models.py:79
127
 msgstr "%(keyword)s pour %(who)s"
123
 msgstr "%(keyword)s pour %(who)s"
128
 
124
 
129
 #: counter/templates/counterTemplate.html:47
125
 #: counter/templates/counterTemplate.html:47
126
+#: counter/templates/homeTemplate.html:32
127
+#: counter/templates/homeTemplate.html:134
130
 msgid "Has not got the seum yet"
128
 msgid "Has not got the seum yet"
131
 msgstr "N'a pas encore eu le seum"
129
 msgstr "N'a pas encore eu le seum"
132
 
130
 
133
 #: counter/templates/counterTemplate.html:51
131
 #: counter/templates/counterTemplate.html:51
132
+#: counter/templates/homeTemplate.html:138
134
 msgid "Has got the seum"
133
 msgid "Has got the seum"
135
 msgstr "A eu le seum"
134
 msgstr "A eu le seum"
136
 
135
 
137
 #: counter/templates/counterTemplate.html:53
136
 #: counter/templates/counterTemplate.html:53
137
+#: counter/templates/homeTemplate.html:140
138
 msgid "threw him/her the seum"
138
 msgid "threw him/her the seum"
139
-msgstr "a foutu le seum"
139
+msgstr "lui a foutu le seum"
140
 
140
 
141
 #: counter/templates/counterTemplate.html:61
141
 #: counter/templates/counterTemplate.html:61
142
-#, fuzzy
143
-#| msgid "reset"
142
+#: counter/templates/homeTemplate.html:45
144
 msgid "Reset"
143
 msgid "Reset"
145
 msgstr "Remise à zéro"
144
 msgstr "Remise à zéro"
146
 
145
 
147
 #: counter/templates/counterTemplate.html:67
146
 #: counter/templates/counterTemplate.html:67
148
 msgid "Motive for the seum:"
147
 msgid "Motive for the seum:"
149
-msgstr "Motif du seum"
148
+msgstr "Motif du seum :"
150
 
149
 
151
 #: counter/templates/counterTemplate.html:74
150
 #: counter/templates/counterTemplate.html:74
151
+#: counter/templates/homeTemplate.html:89
152
 msgid "Throw the seum"
152
 msgid "Throw the seum"
153
 msgstr "Foutre le seum"
153
 msgstr "Foutre le seum"
154
 
154
 
171
 
171
 
172
 #: counter/templates/counterTemplate.html:111
172
 #: counter/templates/counterTemplate.html:111
173
 #: counter/templates/hashtagTemplate.html:27
173
 #: counter/templates/hashtagTemplate.html:27
174
+#: counter/templates/homeTemplate.html:82
174
 msgid "Motive"
175
 msgid "Motive"
175
 msgstr "Motif"
176
 msgstr "Motif"
176
 
177
 
216
 "mais jouissif, peut-être désactivé ou réactivé par la suite."
217
 "mais jouissif, peut-être désactivé ou réactivé par la suite."
217
 
218
 
218
 #: counter/templates/createUser.html:31
219
 #: counter/templates/createUser.html:31
219
-#, fuzzy
220
-#| msgid "email notifications"
221
 msgid "Email notifications"
220
 msgid "Email notifications"
222
 msgstr "Notifications par email"
221
 msgstr "Notifications par email"
223
 
222
 
229
 "Les autres utilisateurs ne pourront voir que ton pseudo et ton trigramme, ce "
228
 "Les autres utilisateurs ne pourront voir que ton pseudo et ton trigramme, ce "
230
 "sera ton identité seumesque !"
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
 msgid "Trigram"
235
 msgid "Trigram"
236
 msgstr "Trigramme"
236
 msgstr "Trigramme"
237
 
237
 
300
 msgid "Victim"
300
 msgid "Victim"
301
 msgstr "Victime"
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
 #: counter/templates/login.html:5
389
 #: counter/templates/login.html:5
304
 msgid "Login"
390
 msgid "Login"
305
 msgstr "Login"
391
 msgstr "Login"
306
 
392
 
307
 #: counter/templates/login.html:17
393
 #: counter/templates/login.html:17
308
-#, fuzzy
309
-#| msgid "Login to access the website!"
310
 msgid "Login to get the seum!"
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
 #: counter/templates/login.html:23
397
 #: counter/templates/login.html:23
314
 msgid "Username"
398
 msgid "Username"
386
 msgid "From %(who)s: %(reason)s"
470
 msgid "From %(who)s: %(reason)s"
387
 msgstr "De %(who)s : %(reason)s"
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
 #: counter/views/user.py:22
520
 #: counter/views/user.py:22
390
 msgid "Passwords do not match."
521
 msgid "Passwords do not match."
391
 msgstr "Les mots de passe sont différents."
522
 msgstr "Les mots de passe sont différents."
405
 #: seum/settings.py:112
536
 #: seum/settings.py:112
406
 msgid "French"
537
 msgid "French"
407
 msgstr "Français"
538
 msgstr "Français"
408
-
409
-#~ msgid "Seum"
410
-#~ msgstr "Seum"

+ 4 - 2
requirements.txt

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