No Description

views.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. from django.shortcuts import render
  2. from counter.models import Counter, Reset
  3. from babel.dates import format_timedelta, format_datetime
  4. from datetime import datetime, timedelta
  5. from django import forms
  6. from django.http import HttpResponseRedirect
  7. from django.core import serializers
  8. from django.core.mail import EmailMessage
  9. from graphos.renderers import gchart
  10. from graphos.sources.simple import SimpleDataSource
  11. from graphos.sources.model import ModelDataSource
  12. import random
  13. import math
  14. import copy
  15. from django.utils import timezone
  16. def home(request):
  17. # JSS above this limit will not be displayed on the col graph
  18. JSS_limit = 7
  19. maxJSS = 0
  20. bestSeumeursNumber = 15
  21. # Display counters
  22. lastResets = []
  23. # Calculates infos for each counter
  24. timezero = timedelta(0)
  25. # First we determine if the session
  26. if 'my-counter' in request.session:
  27. chooseCounter = False
  28. try:
  29. myCounter = Counter.objects.get(id=request.session['my-counter'])
  30. lastReset = Reset.objects.filter(
  31. counter=myCounter).order_by('-timestamp')
  32. if (lastReset.count() == 0):
  33. # This person never had the seum
  34. myCounter.lastReset = Reset()
  35. myCounter.lastReset.delta = timezero
  36. myCounter.lastReset.noSeum = True
  37. else:
  38. myCounter.lastReset = lastReset[0]
  39. myCounter.lastReset.noSeum = False
  40. myCounter.lastReset.delta = datetime.now(
  41. ) - myCounter.lastReset.timestamp.replace(tzinfo=None)
  42. myCounter.seumCount = Reset.objects.filter(
  43. counter=myCounter).count()
  44. myCounter.lastReset.formatted_delta = format_timedelta(
  45. myCounter.lastReset.delta, locale='fr', threshold=1)
  46. except ObjectDoesNotExist:
  47. chooseCounter = True
  48. else:
  49. myCounter = None
  50. chooseCounter = True
  51. counters = Counter.objects.all()
  52. for counter in counters:
  53. lastReset = Reset.objects.filter(
  54. counter=counter).order_by('-timestamp')
  55. if (lastReset.count() == 0):
  56. # This person never had the seum
  57. counter.lastReset = Reset()
  58. counter.lastReset.delta = timezero
  59. counter.lastReset.noSeum = True
  60. counter.CSSclass = "warning"
  61. else:
  62. counter.lastReset = lastReset[0]
  63. counter.lastReset.noSeum = False
  64. counter.lastReset.delta = datetime.now(
  65. ) - counter.lastReset.timestamp.replace(tzinfo=None)
  66. if ((counter.lastReset.delta.total_seconds()) / (24 * 3600) <
  67. JSS_limit):
  68. # Less than 7 JSS -> display on graph
  69. lastResets.append(
  70. [counter.trigramme,
  71. {'v': (counter.lastReset.delta.total_seconds()) /
  72. (24 * 3600),
  73. 'f': str(round(
  74. (counter.lastReset.delta.total_seconds()) /
  75. (24 * 3600), 1))}])
  76. # Updating the max JSS displayed on the graph to compute scale
  77. if (counter.lastReset.delta.total_seconds() / (24 * 3600) >
  78. maxJSS):
  79. maxJSS = (counter.lastReset.delta.total_seconds() /
  80. (24 * 3600))
  81. # Defining CSS attributes for the counter
  82. counter.CSSclass = "primary"
  83. counter.opacity = 0.3 + 0.7 * \
  84. math.exp(-(counter.lastReset.delta.total_seconds()) /
  85. (7 * 24 * 3600))
  86. # Computing the total number of resets for this counter
  87. counter.seumCount = Reset.objects.filter(
  88. counter=counter).count()
  89. counter.lastReset.formatted_delta = format_timedelta(
  90. counter.lastReset.delta, locale='fr', threshold=1)
  91. counter.isHidden = "hidden"
  92. counters = sorted(counters, key=lambda t: t.lastReset.delta)
  93. # Column graph
  94. if (len(lastResets) == 0):
  95. noGraph = True
  96. col_chart = None
  97. else:
  98. noGraph = False
  99. lastResets.sort(key=lambda x: x[1]['v'])
  100. lastResets.insert(0, ['Trigramme', 'Jours sans seum'])
  101. col_data = SimpleDataSource(lastResets)
  102. col_chart = gchart.ColumnChart(col_data, options={
  103. 'title': '',
  104. 'legend': 'none',
  105. 'vAxis': {
  106. 'viewWindow': {
  107. 'max': max(maxJSS, 1),
  108. 'min': 0
  109. },
  110. 'ticks': [1, 2, 3, 4, 5, 6, 7],
  111. 'title': 'Jours sans seum'
  112. },
  113. 'hAxis': {'title': 'Trigramme'},
  114. })
  115. # Timeline graph
  116. # Data pre-processing
  117. resets = Reset.objects.filter(
  118. timestamp__gte=timezone.now() - timedelta(days=1))
  119. if (resets.count() == 0):
  120. noTimeline = True
  121. line_chart = None
  122. else:
  123. noTimeline = False
  124. for reset in resets:
  125. reset.timestamp = {
  126. 'v': reset.timestamp.timestamp(),
  127. 'f': "Il y a " + format_timedelta(datetime.now() -
  128. reset.timestamp.replace(
  129. tzinfo=None),
  130. locale='fr', threshold=1)
  131. }
  132. reset.Seum = {
  133. 'v': 0, 'f': reset.counter.trigramme + " : " + reset.reason}
  134. # Drawing the graph
  135. line_data = ModelDataSource(resets, fields=['timestamp', 'Seum'])
  136. line_chart = gchart.LineChart(line_data, options={
  137. 'lineWidth': 0,
  138. 'pointSize': 10,
  139. 'title': '',
  140. 'vAxis': {'ticks': []},
  141. 'hAxis': {
  142. 'ticks': [
  143. {'v': (datetime.now() - timedelta(days=1)
  144. ).timestamp(), 'f': 'Il y a 24 h'},
  145. {'v': datetime.now().timestamp(), 'f': 'Présent'}
  146. ]
  147. },
  148. 'legend': 'none',
  149. 'height': 90
  150. })
  151. # Graph of greatest seumers
  152. seumCounts = []
  153. for counter in counters:
  154. seumCounts.append([counter.trigramme, Reset.objects.filter(
  155. counter=counter).count()])
  156. if (len(seumCounts) == 0):
  157. noBestSeum = True
  158. best_chart = None
  159. else:
  160. seumCounts.sort(key=lambda x: -x[1])
  161. noBestSeum = False
  162. seumCounts.insert(0, ['Trigramme', 'Nombre de seums'])
  163. best_data = SimpleDataSource(seumCounts[:bestSeumeursNumber])
  164. best_chart = gchart.ColumnChart(best_data, options={
  165. 'title': '',
  166. 'legend': 'none',
  167. 'vAxis': {'title': 'Nombre de seums'},
  168. 'hAxis': {'title': 'Trigramme'},
  169. })
  170. # Graph of seum activity
  171. resets = Reset.objects.filter(
  172. timestamp__gte=timezone.now() - timedelta(days=365))
  173. months = {}
  174. for reset in resets:
  175. monthDate = datetime(reset.timestamp.year, reset.timestamp.month, 1)
  176. months[monthDate] = months.get(monthDate, 0) + 1
  177. monthList = sorted(months.items(), key=lambda t: t[0])
  178. seumActivity = []
  179. for month in monthList:
  180. seumActivity.append(
  181. [format_datetime(month[0], locale='fr',
  182. format="MMM Y").capitalize(), month[1]])
  183. if (len(seumActivity) == 0):
  184. noSeumActivity = True
  185. activity_chart = None
  186. else:
  187. noSeumActivity = False
  188. seumActivity.insert(0, ['Mois', 'Nombre de seums'])
  189. activity_data = SimpleDataSource(seumActivity)
  190. activity_chart = gchart.ColumnChart(activity_data, options={
  191. 'title': '',
  192. 'legend': 'none',
  193. 'vAxis': {'title': 'Nombre de seums'},
  194. 'hAxis': {'title': 'Mois'},
  195. })
  196. return render(request, 'homeTemplate.html', {
  197. 'counters': counters,
  198. 'col_chart': col_chart,
  199. 'line_chart': line_chart,
  200. 'best_chart': best_chart,
  201. 'activity_chart': activity_chart,
  202. 'noTimeline': noTimeline,
  203. 'noGraph': noGraph,
  204. 'noBestSeum': noBestSeum,
  205. 'noSeumActivity': noSeumActivity,
  206. 'chooseCounter': chooseCounter,
  207. 'myCounter': myCounter,
  208. })
  209. def resetCounter(request):
  210. # Update Form counter
  211. if (request.method == 'POST'):
  212. # create a form instance and populate it with data from the request:
  213. data = dict(request.POST)
  214. counter = Counter.objects.get(pk=int(data['counter'][0]))
  215. reset = Reset()
  216. reset.counter = counter
  217. reset.reason = data['reason'][0]
  218. reset.timestamp = datetime.now()
  219. reset.save()
  220. # We send the emails only to those who have an email address
  221. emails = [u[0] for u in Counter.objects.all().values_list('email')
  222. if u[0] != 'null@localhost']
  223. # Now send emails to everyone
  224. email_to_send = EmailMessage('[SeumBook] '+counter.name + ' a le seum',
  225. data['reason'][0] + '''
  226. --
  227. SeumBook™ - http://seum.merigoux.ovh
  228. P.S. : Pour ne plus recevoir ces messages, envoie un mail à denis.merigoux@gmail.com''',
  229. 'SeumMan <seum@merigoux.ovh>', emails, [],
  230. reply_to=emails)
  231. email_to_send.send()
  232. return HttpResponseRedirect(data['redirect'][0])
  233. def counter(request, id_counter):
  234. counter = Counter.objects.get(pk=id_counter)
  235. resets = Reset.objects.filter(counter=counter).order_by('-timestamp')
  236. firstReset = copy.copy(resets[len(resets) - 1])
  237. timezero = timedelta(0)
  238. # Display
  239. if (resets.count() == 0):
  240. counter.lastReset = Reset()
  241. counter.lastReset.delta = timezero
  242. counter.lastReset.noSeum = True
  243. seumFrequency = 'inconnu'
  244. else:
  245. counter.lastReset = resets[0]
  246. counter.lastReset.noSeum = False
  247. counter.lastReset.delta = datetime.now(
  248. ) - counter.lastReset.timestamp.replace(tzinfo=None)
  249. counter.lastReset.formatted_delta = format_timedelta(
  250. counter.lastReset.delta, locale='fr', threshold=1)
  251. counter.seumCount = Reset.objects.filter(
  252. counter=counter).count()
  253. seumFrequency = format_timedelta((
  254. datetime.now() - firstReset.timestamp.replace(tzinfo=None)) /
  255. counter.seumCount, locale='fr', threshold=1)
  256. for reset in resets:
  257. reset.date = format_datetime(
  258. reset.timestamp, locale='fr',
  259. format="EEEE dd MMMM Y 'à' HH:mm").capitalize()
  260. # Timeline graph
  261. # Data pre-processing
  262. resets_graph = resets
  263. for reset in resets_graph:
  264. reset.timestamp = {
  265. 'v': reset.timestamp.timestamp(),
  266. 'f': "Il y a " + format_timedelta(
  267. datetime.now() - reset.timestamp.replace(tzinfo=None),
  268. locale='fr', threshold=1)
  269. }
  270. reset.Seum = {'v': 0, 'f': reset.reason}
  271. # Drawing the graph
  272. data = ModelDataSource(resets, fields=['timestamp', 'Seum'])
  273. chart = gchart.LineChart(data, options={
  274. 'lineWidth': 0,
  275. 'pointSize': 10,
  276. 'title': '',
  277. 'vAxis': {'ticks': []},
  278. 'hAxis': {'ticks': [{
  279. 'v': firstReset.timestamp.timestamp(),
  280. 'f': 'Il y a ' + format_timedelta(
  281. datetime.now() - firstReset.timestamp.replace(tzinfo=None),
  282. locale='fr', threshold=1)
  283. }, {
  284. 'v': datetime.now().timestamp(),
  285. 'f': 'Présent'}
  286. ]},
  287. 'legend': 'none',
  288. 'height': 90
  289. })
  290. return render(request, 'counterTemplate.html', {
  291. 'counter': counter,
  292. 'chart': chart,
  293. 'resets': resets,
  294. 'seumFrequency': seumFrequency
  295. })
  296. def setMyCounter(request):
  297. if (request.method == 'POST'):
  298. # create a form instance and populate it with data from the request:
  299. data = dict(request.POST)
  300. # We put the id of the counter in the session
  301. request.session['my-counter'] = data['myCounter'][0]
  302. return HttpResponseRedirect(data['redirect'][0])
  303. def resetMyCounter(request):
  304. if (request.method == 'POST'):
  305. data = dict(request.POST)
  306. del request.session['my-counter']
  307. return HttpResponseRedirect(data['redirect'][0])