暂无描述

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