No Description

views.py 12KB

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