Keine Beschreibung

views.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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.mail import EmailMessage
  8. from graphos.renderers import gchart
  9. from graphos.sources.simple import SimpleDataSource
  10. from graphos.sources.model import ModelDataSource
  11. import random
  12. import math
  13. import copy
  14. from django.utils import timezone
  15. def home(request):
  16. # JSS above this limit will not be displayed on the col graph
  17. JSS_limit = 7
  18. maxJSS = 0
  19. bestSeumeursNumber = 15
  20. # Display counters
  21. lastResets = []
  22. # Calculates infos for each counter
  23. timezero = timedelta(0)
  24. # First we determine if the session
  25. if 'my-counter' in request.session:
  26. chooseCounter = False
  27. try:
  28. myCounter = Counter.objects.get(id=request.session['my-counter'])
  29. lastReset = Reset.objects.filter(
  30. counter=myCounter).order_by('-timestamp')
  31. if (lastReset.count() == 0):
  32. # This person never had the seum
  33. myCounter.lastReset = Reset()
  34. myCounter.lastReset.delta = timezero
  35. myCounter.lastReset.noSeum = True
  36. else:
  37. myCounter.lastReset = lastReset[0]
  38. myCounter.lastReset.noSeum = False
  39. myCounter.lastReset.delta = datetime.now(
  40. ) - myCounter.lastReset.timestamp.replace(tzinfo=None)
  41. myCounter.seumCount = Reset.objects.filter(
  42. counter=myCounter).count()
  43. myCounter.lastReset.formatted_delta = format_timedelta(
  44. myCounter.lastReset.delta, locale='fr', threshold=1)
  45. except ObjectDoesNotExist:
  46. chooseCounter = True
  47. else:
  48. myCounter = None
  49. chooseCounter = True
  50. # Building data for counters display
  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])