Brak opisu

views.py 11KB

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