瀏覽代碼

Added badge for number of seums for each counter, beautified code

Denis Merigoux 9 年之前
父節點
當前提交
6c99685c18
共有 3 個文件被更改,包括 149 次插入97 次删除
  1. 4 2
      counter/templates/counterTemplate.html
  2. 7 7
      counter/templates/homeTemplate.html
  3. 138 88
      counter/views.py

+ 4 - 2
counter/templates/counterTemplate.html

1
 {% extends 'baseTemplate.html' %} {% block title %}{{counter.trigramme}}{% endblock %} {% block content %}
1
 {% extends 'baseTemplate.html' %} {% block title %}{{counter.trigramme}}{% endblock %} {% block content %}
2
 <div class="text-center">
2
 <div class="text-center">
3
-  <h1>{{counter.trigramme}} <small>{{ counter.name }}</small></h1>
3
+  <h1>
4
+      {{counter.trigramme}} <small>{{ counter.name }}</small>
5
+  </h1>
4
 </div>
6
 </div>
5
 <div class="container-fluid">
7
 <div class="container-fluid">
6
   <div class="row">
8
   <div class="row">
7
     <div class="col-md-3">
9
     <div class="col-md-3">
8
       <div class="panel panel-primary">
10
       <div class="panel panel-primary">
9
         <div class="panel-heading">
11
         <div class="panel-heading">
10
-          <h2 class="panel-title">Dernier seum</h2>
12
+          <h2 class="panel-title">Dernier seum {% if not counter.lastReset.noSeum %}<span class="badge pull-right">{{ counter.seumCount }}</span>{% endif %}</h2>
11
         </div>
13
         </div>
12
         <div class="seum-counter panel-body">
14
         <div class="seum-counter panel-body">
13
           {% if counter.lastReset.noSeum %}
15
           {% if counter.lastReset.noSeum %}

+ 7 - 7
counter/templates/homeTemplate.html

8
 		<div class="col-md-4 col-sm-6 col-lg-3">
8
 		<div class="col-md-4 col-sm-6 col-lg-3">
9
 			<div class="panel panel-{{counter.CSSclass}}" style="opacity:{{counter.opacity}}">
9
 			<div class="panel panel-{{counter.CSSclass}}" style="opacity:{{counter.opacity}}">
10
 				<div class="panel-heading">
10
 				<div class="panel-heading">
11
-					<a class="counter-link" href="{% url 'counter' id_counter=counter.id %}"><h2 class="panel-title">{{ counter.trigramme }} <small>{{ counter.name }}</small></h2></a>
11
+					<a class="counter-link" href="{% url 'counter' id_counter=counter.id %}">
12
+						<h2 class="panel-title">{{ counter.trigramme }} <small>{{ counter.name }}</small>
13
+							{% if not counter.lastReset.noSeum %}<span class="pull-right badge">{{ counter.seumCount }}</span>{% endif %}
14
+						</h2>
15
+					</a>
12
 				</div>
16
 				</div>
13
 				<div class="seum-counter panel-body">
17
 				<div class="seum-counter panel-body">
14
 					{% if counter.lastReset.noSeum %}
18
 					{% if counter.lastReset.noSeum %}
49
 					<div class="text-center text-muted">
53
 					<div class="text-center text-muted">
50
 						<p>Personne n'a le seum...</p>
54
 						<p>Personne n'a le seum...</p>
51
 					</div>
55
 					</div>
52
-					{% else %}
53
-					{{ col_chart.as_html }}
54
-					{% endif %}
56
+					{% else %} {{ col_chart.as_html }} {% endif %}
55
 				</div>
57
 				</div>
56
 			</div>
58
 			</div>
57
 		</div>
59
 		</div>
67
 					<div class="text-center text-muted">
69
 					<div class="text-center text-muted">
68
 						<p>Pas de seum dans les dernières 24 heures...</p>
70
 						<p>Pas de seum dans les dernières 24 heures...</p>
69
 					</div>
71
 					</div>
70
-					{% else %}
71
-					{{ line_chart.as_html }}
72
-					{% endif %}
72
+					{% else %} {{ line_chart.as_html }} {% endif %}
73
 				</div>
73
 				</div>
74
 			</div>
74
 			</div>
75
 		</div>
75
 		</div>

+ 138 - 88
counter/views.py

1
 from django.shortcuts import render
1
 from django.shortcuts import render
2
-from counter.models import Counter,Reset
2
+from counter.models import Counter, Reset
3
 from babel.dates import format_timedelta, format_datetime
3
 from babel.dates import format_timedelta, format_datetime
4
-from datetime import datetime,timedelta
4
+from datetime import datetime, timedelta
5
 from django import forms
5
 from django import forms
6
 from django.http import HttpResponseRedirect
6
 from django.http import HttpResponseRedirect
7
 from django.core import serializers
7
 from django.core import serializers
13
 import math
13
 import math
14
 from django.utils import timezone
14
 from django.utils import timezone
15
 
15
 
16
+
16
 class resetCounterForm(forms.ModelForm):
17
 class resetCounterForm(forms.ModelForm):
18
+
17
     class Meta:
19
     class Meta:
18
         model = Reset
20
         model = Reset
19
-        fields = ['reason','counter']
21
+        fields = ['reason', 'counter']
22
+
20
 
23
 
21
 def home(request):
24
 def home(request):
22
-    #JSS above this limit will not be displayed on the col graph
25
+    # JSS above this limit will not be displayed on the col graph
23
     JSS_limit = 7
26
     JSS_limit = 7
24
-    #Display counters
27
+    # Display counters
25
     counters = Counter.objects.all()
28
     counters = Counter.objects.all()
26
     lastResets = []
29
     lastResets = []
27
-    #Calculates infos for each counter
30
+    # Calculates infos for each counter
28
     maxJSS = 0
31
     maxJSS = 0
29
     timezero = timedelta(0)
32
     timezero = timedelta(0)
30
     for counter in counters:
33
     for counter in counters:
31
-        lastReset = Reset.objects.filter(counter=counter).order_by('-timestamp')
34
+        lastReset = Reset.objects.filter(
35
+            counter=counter).order_by('-timestamp')
32
         if (lastReset.count() == 0):
36
         if (lastReset.count() == 0):
37
+            # This person never had the seum
33
             counter.lastReset = Reset()
38
             counter.lastReset = Reset()
34
             counter.lastReset.delta = timezero
39
             counter.lastReset.delta = timezero
35
             counter.lastReset.noSeum = True
40
             counter.lastReset.noSeum = True
37
         else:
42
         else:
38
             counter.lastReset = lastReset[0]
43
             counter.lastReset = lastReset[0]
39
             counter.lastReset.noSeum = False
44
             counter.lastReset.noSeum = False
40
-            counter.lastReset.delta = datetime.now()-counter.lastReset.timestamp.replace(tzinfo=None)
41
-            if ((counter.lastReset.delta.total_seconds())/(24*3600)<JSS_limit):
42
-                #If more thant 7 JSS do not display on graph
43
-                lastResets.append([counter.trigramme,{'v' : (counter.lastReset.delta.total_seconds())/(24*3600), 'f' : str(round((counter.lastReset.delta.total_seconds())/(24*3600),1))} ])
44
-                counter.CSSclass = "primary"
45
-                if (counter.lastReset.delta.total_seconds())/(24*3600) > maxJSS:
46
-                    maxJSS = (counter.lastReset.delta.total_seconds())/(24*3600)
47
-            else:
48
-                counter.CSSclass = "primary"
49
-            counter.opacity = 0.3 + 0.7*math.exp(-(counter.lastReset.delta.total_seconds())/(7*24*3600))
50
-        counter.lastReset.formatted_delta = format_timedelta(counter.lastReset.delta,locale='fr',threshold=1)
45
+            counter.lastReset.delta = datetime.now(
46
+            ) - counter.lastReset.timestamp.replace(tzinfo=None)
47
+            if ((counter.lastReset.delta.total_seconds()) / (24 * 3600) <
48
+                    JSS_limit):
49
+                # Less than 7 JSS -> display on graph
50
+                lastResets.append(
51
+                    [counter.trigramme,
52
+                     {'v': (counter.lastReset.delta.total_seconds()) /
53
+                      (24 * 3600),
54
+                      'f': str(round(
55
+                          (counter.lastReset.delta.total_seconds()) /
56
+                          (24 * 3600), 1))}])
57
+                # Updating the max JSS displayed on the graph to compute scale
58
+                if (counter.lastReset.delta.total_seconds() / (24 * 3600) >
59
+                        maxJSS):
60
+                    maxJSS = (counter.lastReset.delta.total_seconds() /
61
+                              (24 * 3600))
62
+            # Defining CSS attributes for the counter
63
+            counter.CSSclass = "primary"
64
+            counter.opacity = 0.3 + 0.7 * \
65
+                math.exp(-(counter.lastReset.delta.total_seconds()) /
66
+                         (7 * 24 * 3600))
67
+            # Computing the total number of resets for this counter
68
+            counter.seumCount = Reset.objects.filter(
69
+                counter=counter).count()
70
+        counter.lastReset.formatted_delta = format_timedelta(
71
+            counter.lastReset.delta, locale='fr', threshold=1)
51
         counter.isHidden = "hidden"
72
         counter.isHidden = "hidden"
52
-    counters = sorted(counters,key=lambda t: t.lastReset.delta)
53
-    #Column graph
54
-    if (len(lastResets) ==0):
73
+    counters = sorted(counters, key=lambda t: t.lastReset.delta)
74
+    # Column graph
75
+    if (len(lastResets) == 0):
55
         noGraph = True
76
         noGraph = True
56
         col_chart = None
77
         col_chart = None
57
     else:
78
     else:
58
         noGraph = False
79
         noGraph = False
59
         lastResets.sort(key=lambda x: x[1]['v'])
80
         lastResets.sort(key=lambda x: x[1]['v'])
60
-        lastResets.insert(0,['Trigramme','Jours sans seum'])
81
+        lastResets.insert(0, ['Trigramme', 'Jours sans seum'])
61
         col_data = SimpleDataSource(lastResets)
82
         col_data = SimpleDataSource(lastResets)
62
-        col_chart = gchart.ColumnChart(col_data,options={
63
-            'title' : '',
64
-            'legend' : 'none',
65
-            'vAxis' : {
66
-                'viewWindow' : {
67
-                    'max' : max(maxJSS,1) ,
68
-                    'min' : 0
69
-                    },
70
-                'ticks' : [1,2,3,4,5,6,7],
71
-                'title' : 'Jours sans seum'
83
+        col_chart = gchart.ColumnChart(col_data, options={
84
+            'title': '',
85
+            'legend': 'none',
86
+            'vAxis': {
87
+                'viewWindow': {
88
+                    'max': max(maxJSS, 1),
89
+                    'min': 0
72
                 },
90
                 },
73
-            'hAxis' : {'title' : 'Trigramme' },
91
+                'ticks': [1, 2, 3, 4, 5, 6, 7],
92
+                'title': 'Jours sans seum'
93
+            },
94
+            'hAxis': {'title': 'Trigramme'},
74
         })
95
         })
75
 
96
 
76
-    ###Timeline graph
77
-    #Data pre-processing
78
-    resets = Reset.objects.filter(timestamp__gte=timezone.now() - timedelta(days=1))
97
+    # Timeline graph
98
+    # Data pre-processing
99
+    resets = Reset.objects.filter(
100
+        timestamp__gte=timezone.now() - timedelta(days=1))
79
     if (resets.count() == 0):
101
     if (resets.count() == 0):
80
         noTimeline = True
102
         noTimeline = True
81
         line_chart = None
103
         line_chart = None
82
     else:
104
     else:
83
         noTimeline = False
105
         noTimeline = False
84
         for reset in resets:
106
         for reset in resets:
85
-            reset.timestamp={
86
-                'v' : reset.timestamp.timestamp(),
87
-                'f' : "Il y a "+format_timedelta(datetime.now()-reset.timestamp.replace(tzinfo=None),locale='fr',threshold=1)
88
-                }
89
-            reset.Seum={'v' : 0, 'f' : reset.counter.trigramme+" : "+reset.reason}
90
-            #Drawing the graph
91
-        line_data = ModelDataSource(resets,fields=['timestamp','Seum'])
107
+            reset.timestamp = {
108
+                'v': reset.timestamp.timestamp(),
109
+                'f': "Il y a " + format_timedelta(datetime.now() -
110
+                                                  reset.timestamp.replace(
111
+                                                      tzinfo=None),
112
+                                                  locale='fr', threshold=1)
113
+            }
114
+            reset.Seum = {
115
+                'v': 0, 'f': reset.counter.trigramme + " : " + reset.reason}
116
+        # Drawing the graph
117
+        line_data = ModelDataSource(resets, fields=['timestamp', 'Seum'])
92
         line_chart = gchart.LineChart(line_data, options={
118
         line_chart = gchart.LineChart(line_data, options={
93
-            'lineWidth' : 0,
94
-            'pointSize' : 10,
95
-            'title' : '',
96
-            'vAxis' : { 'ticks' : []},
97
-            'hAxis' : {
98
-                'ticks' : [
99
-                        {'v' : (datetime.now() - timedelta(days=1)).timestamp(), 'f' : 'Il y a 24 h' },
100
-                        { 'v' :datetime.now().timestamp(), 'f' : 'Présent'}
101
-                    ]
102
-                },
103
-            'legend' : 'none',
104
-            'height' : 90
105
-            })
106
-
107
-    return render(request,'homeTemplate.html', {
108
-        'counters' : counters,
109
-        'col_chart' : col_chart,
110
-        'line_chart' : line_chart,
111
-        'noTimeline' : noTimeline,
112
-        'noGraph' : noGraph
119
+            'lineWidth': 0,
120
+            'pointSize': 10,
121
+            'title': '',
122
+            'vAxis': {'ticks': []},
123
+            'hAxis': {
124
+                'ticks': [
125
+                    {'v': (datetime.now() - timedelta(days=1)
126
+                           ).timestamp(), 'f': 'Il y a 24 h'},
127
+                    {'v': datetime.now().timestamp(), 'f': 'Présent'}
128
+                ]
129
+            },
130
+            'legend': 'none',
131
+            'height': 90
113
         })
132
         })
114
 
133
 
134
+    return render(request, 'homeTemplate.html', {
135
+        'counters': counters,
136
+        'col_chart': col_chart,
137
+        'line_chart': line_chart,
138
+        'noTimeline': noTimeline,
139
+        'noGraph': noGraph
140
+    })
141
+
142
+
115
 def resetCounter(request):
143
 def resetCounter(request):
116
-    #Update Form counter
144
+    # Update Form counter
117
     if (request.method == 'POST'):
145
     if (request.method == 'POST'):
118
         # create a form instance and populate it with data from the request:
146
         # create a form instance and populate it with data from the request:
119
         data = dict(request.POST)
147
         data = dict(request.POST)
120
-        counter =  Counter.objects.get(pk=int(data['counter'][0]))
148
+        counter = Counter.objects.get(pk=int(data['counter'][0]))
121
         reset = Reset()
149
         reset = Reset()
122
         reset.counter = counter
150
         reset.counter = counter
123
         reset.reason = data['reason'][0]
151
         reset.reason = data['reason'][0]
124
         reset.timestamp = datetime.now()
152
         reset.timestamp = datetime.now()
125
         reset.save()
153
         reset.save()
126
-        emails = [u[0] for u in Counter.objects.all().values_list('email') if u[0] != 'null@localhost']
127
-        #Now send emails to everyone
128
-        email_to_send = EmailMessage( counter.name+' a le seum', data['reason'][0]+'''
154
+        # We send the emails only to those who have an email address
155
+        emails = [u[0] for u in Counter.objects.all().values_list('email')
156
+                  if u[0] != 'null@localhost']
157
+        # Now send emails to everyone
158
+        email_to_send = EmailMessage(counter.name + ' a le seum',
159
+                                     data['reason'][0] + '''
129
 
160
 
130
 --
161
 --
131
 SeumBook™ - http://seum.merigoux.ovh
162
 SeumBook™ - http://seum.merigoux.ovh
132
 
163
 
133
 P.S. : Pour ne plus recevoir ces messages, envoie un mail à denis.merigoux@gmail.com''',
164
 P.S. : Pour ne plus recevoir ces messages, envoie un mail à denis.merigoux@gmail.com''',
134
-        'SeumMan <seum@merigoux.ovh>', emails,[],reply_to=emails)
165
+                                     'SeumMan <seum@merigoux.ovh>', emails, [],
166
+                                     reply_to=emails)
135
     email_to_send.send()
167
     email_to_send.send()
136
 
168
 
137
     return HttpResponseRedirect(data['redirect'][0])
169
     return HttpResponseRedirect(data['redirect'][0])
138
 
170
 
171
+
139
 def counter(request, id_counter):
172
 def counter(request, id_counter):
140
 
173
 
141
     counter = Counter.objects.get(pk=id_counter)
174
     counter = Counter.objects.get(pk=id_counter)
142
     resets = Reset.objects.filter(counter=counter).order_by('-timestamp')
175
     resets = Reset.objects.filter(counter=counter).order_by('-timestamp')
143
     timezero = timedelta(0)
176
     timezero = timedelta(0)
144
-    #Display
177
+    # Display
145
     if (resets.count() == 0):
178
     if (resets.count() == 0):
146
         counter.lastReset = Reset()
179
         counter.lastReset = Reset()
147
         counter.lastReset.delta = timezero
180
         counter.lastReset.delta = timezero
149
     else:
182
     else:
150
         counter.lastReset = resets[0]
183
         counter.lastReset = resets[0]
151
         counter.lastReset.noSeum = False
184
         counter.lastReset.noSeum = False
152
-        counter.lastReset.delta = datetime.now()-counter.lastReset.timestamp.replace(tzinfo=None)
153
-        counter.lastReset.formatted_delta = format_timedelta(counter.lastReset.delta,locale='fr',threshold=1)
185
+        counter.lastReset.delta = datetime.now(
186
+        ) - counter.lastReset.timestamp.replace(tzinfo=None)
187
+        counter.lastReset.formatted_delta = format_timedelta(
188
+            counter.lastReset.delta, locale='fr', threshold=1)
189
+        counter.seumCount = Reset.objects.filter(
190
+            counter=counter).count()
154
 
191
 
155
     for reset in resets:
192
     for reset in resets:
156
-        reset.date = format_datetime(reset.timestamp,locale='fr',format="EEEE dd MMMM Y 'à' HH:mm:ss").capitalize()
157
-    ###Timeline graph
158
-    #Data pre-processing
159
-    resets_graph=resets
193
+        reset.date = format_datetime(
194
+            reset.timestamp, locale='fr',
195
+            format="EEEE dd MMMM Y 'à' HH:mm:ss").capitalize()
196
+    # Timeline graph
197
+    # Data pre-processing
198
+    resets_graph = resets
160
     for reset in resets_graph:
199
     for reset in resets_graph:
161
-        reset.timestamp={'v' : reset.timestamp.timestamp(), 'f' : "Il y a "+format_timedelta(datetime.now()-reset.timestamp.replace(tzinfo=None),locale='fr',threshold=1) }
162
-        reset.Seum={'v' : 0, 'f' : reset.reason}
163
-    #Drawing the graph
164
-    data = ModelDataSource(resets,fields=['timestamp','Seum'])
200
+        reset.timestamp = {
201
+            'v': reset.timestamp.timestamp(),
202
+            'f': "Il y a " + format_timedelta(
203
+                datetime.now() - reset.timestamp.replace(tzinfo=None),
204
+                locale='fr', threshold=1)
205
+        }
206
+        reset.Seum = {'v': 0, 'f': reset.reason}
207
+    # Drawing the graph
208
+    data = ModelDataSource(resets, fields=['timestamp', 'Seum'])
165
     chart = gchart.LineChart(data, options={
209
     chart = gchart.LineChart(data, options={
166
-        'lineWidth' : 0,
167
-        'pointSize' : 10,
168
-        'title' : '',
169
-        'vAxis' : { 'ticks' : []},
170
-        'hAxis' : {'ticks' : [{'v' : datetime(2016,3,9,23,0,0,0).timestamp(), 'f' : 'ADD des X2013' }, { 'v' :datetime.now().timestamp(), 'f' : 'Présent'}]},
171
-        'legend' : 'none',
172
-        'height' : 90
210
+        'lineWidth': 0,
211
+        'pointSize': 10,
212
+        'title': '',
213
+        'vAxis': {'ticks': []},
214
+        'hAxis': {'ticks': [{
215
+            'v': datetime(2016, 3, 9, 23, 0, 0, 0).timestamp(),
216
+            'f': 'ADD des X2013'
217
+        }, {
218
+            'v': datetime.now().timestamp(),
219
+            'f': 'Présent'}
220
+        ]},
221
+        'legend': 'none',
222
+        'height': 90
173
     })
223
     })
174
 
224
 
175
-    return render(request,'counterTemplate.html', { 'counter' : counter, 'chart' : chart, 'resets' : resets })
225
+    return render(request, 'counterTemplate.html', {'counter': counter, 'chart': chart, 'resets': resets})