Ei kuvausta

views.py 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. from django.shortcuts import render
  2. from django.utils.translation import ugettext as _
  3. from counter.models import Counter, Reset, Like, Keyword, Hashtag
  4. from django.contrib.auth.models import User
  5. from babel.dates import format_timedelta, format_datetime
  6. from datetime import datetime, timedelta
  7. from django import forms
  8. from django.http import HttpResponseRedirect
  9. from django.core.mail import EmailMessage
  10. from django.contrib.auth.decorators import login_required
  11. from django.core.urlresolvers import reverse
  12. from django.db import IntegrityError
  13. from graphos.renderers import gchart
  14. from django.template.loader import render_to_string
  15. from graphos.sources.simple import SimpleDataSource
  16. from graphos.sources.model import ModelDataSource
  17. import random
  18. import math
  19. import copy
  20. import functools
  21. from django.utils import timezone
  22. from counter.utils import parseSeumReason
  23. # Number of counters displayed on the home page's best seumeurs graph
  24. bestSeumeursNumber = 15
  25. @login_required
  26. def home(request):
  27. # Used later to keep track of the maximum JSS
  28. lastResets = []
  29. no_seum_delta = timedelta.max
  30. # First select our counter
  31. try:
  32. myCounter = Counter.objects.get(user__id=request.user.id)
  33. lastReset = Reset.objects.filter(
  34. counter=myCounter).order_by('-timestamp')
  35. if (lastReset.count() == 0):
  36. # This person never had the seum
  37. myCounter.lastReset = Reset()
  38. myCounter.lastReset.delta = no_seum_delta
  39. myCounter.lastReset.noSeum = True
  40. else:
  41. myCounter.lastReset = lastReset[0]
  42. myCounter.lastReset.noSeum = False
  43. if (myCounter.lastReset.who is None or
  44. myCounter.lastReset.who.id == myCounter.id):
  45. myCounter.lastReset.selfSeum = True
  46. else:
  47. myCounter.lastReset.selfSeum = False
  48. myCounter.lastReset.delta = datetime.now(
  49. ) - myCounter.lastReset.timestamp.replace(tzinfo=None)
  50. likesMe = Like.objects.filter(
  51. reset=myCounter.lastReset)
  52. myCounter.likeCount = likesMe.count()
  53. if myCounter.likeCount:
  54. myCounter.likersString = functools.reduce(
  55. lambda a, b: a + ", " + b,
  56. [like.liker.trigramme for like in likesMe])
  57. myCounter.lastReset.formatted_delta = format_timedelta(
  58. myCounter.lastReset.delta, locale='fr', threshold=1)
  59. except Counter.DoesNotExist:
  60. return HttpResponseRedirect(reverse('login'))
  61. # Building data for counters display
  62. counters = Counter.objects.all()
  63. for counter in counters:
  64. # Only the last reset is displayed
  65. lastReset = Reset.objects.filter(
  66. counter=counter).order_by('-timestamp')
  67. if (lastReset.count() == 0): # This person never had the seum
  68. counter.lastReset = Reset()
  69. counter.lastReset.delta = no_seum_delta
  70. counter.lastReset.noSeum = True
  71. counter.CSSclass = "warning"
  72. counter.likeCount = -1
  73. else: # This person already had the seum
  74. counter.lastReset = lastReset[0]
  75. # To display the last seum we have to know if it is self-inflicted
  76. if (counter.lastReset.who is None or
  77. counter.lastReset.who.id == counter.id):
  78. counter.lastReset.selfSeum = True
  79. else:
  80. counter.lastReset.selfSeum = False
  81. # Now we compute the duration since the reset
  82. counter.lastReset.noSeum = False
  83. counter.lastReset.delta = datetime.now(
  84. ) - counter.lastReset.timestamp.replace(tzinfo=None)
  85. # Defining CSS attributes for the counter
  86. if counter.id == myCounter.id:
  87. counter.CSSclass = 'primary'
  88. else:
  89. counter.CSSclass = 'default'
  90. # Computing the total number of likes for this counter
  91. likesMe = Like.objects.filter(
  92. reset=counter.lastReset)
  93. counter.likeCount = likesMe.count()
  94. counter.alreadyLiked = (Like.objects.filter(
  95. reset=counter.lastReset, liker=myCounter).exists())
  96. if counter.likeCount > 0:
  97. counter.likersString = functools.reduce(
  98. lambda a, b: a + ", " + b,
  99. [like.liker.trigramme for like in likesMe])
  100. counter.lastReset.formatted_delta = format_timedelta(
  101. counter.lastReset.delta, locale='fr', threshold=1)
  102. counter.isHidden = 'hidden'
  103. if myCounter.sort_by_score:
  104. # Now we sort the counters according to a reddit-like ranking formula
  105. # We take into account the number of likes of a reset and recentness
  106. # The log on the score will give increased value to the first likes
  107. # The counters with no seum have a like count of -1 by convention
  108. counters = sorted(counters, key=lambda t: - (
  109. math.log(t.likeCount + 2) /
  110. (1 + (t.lastReset.delta.total_seconds()) /
  111. (24 * 3600))))
  112. else:
  113. counters = sorted(counters, key=lambda t: +
  114. t.lastReset.delta.total_seconds())
  115. # Timeline graph
  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. if (reset.who is None or
  132. reset.who.id == reset.counter.id):
  133. reset.Seum = {'v': 0,
  134. 'f': reset.counter.trigramme +
  135. " : " + reset.reason}
  136. else:
  137. reset.Seum = {'v': 0,
  138. 'f': reset.who.trigramme + ' à ' +
  139. reset.counter.trigramme +
  140. " : " + reset.reason}
  141. line_data = ModelDataSource(resets, fields=['timestamp', 'Seum'])
  142. line_chart = gchart.LineChart(line_data, options={
  143. 'lineWidth': 0,
  144. 'pointSize': 10,
  145. 'title': '',
  146. 'vAxis': {'ticks': []},
  147. 'hAxis': {
  148. 'ticks': [
  149. {'v': (datetime.now() - timedelta(days=1)
  150. ).timestamp(), 'f': 'Il y a 24 h'},
  151. {'v': datetime.now().timestamp(), 'f': 'Présent'}
  152. ]
  153. },
  154. 'legend': 'none',
  155. 'height': 90
  156. })
  157. # Graph of greatest seumers
  158. seumCounts = []
  159. for counter in counters:
  160. seumCounts.append([counter.trigramme, Reset.objects.filter(
  161. counter=counter).count()])
  162. if (len(seumCounts) == 0):
  163. noBestSeum = True
  164. best_chart = None
  165. else:
  166. seumCounts.sort(key=lambda x: -x[1])
  167. noBestSeum = False
  168. seumCounts.insert(0, ['Trigramme', 'Nombre de seums'])
  169. best_data = SimpleDataSource(seumCounts[:bestSeumeursNumber])
  170. best_chart = gchart.ColumnChart(best_data, options={
  171. 'title': '',
  172. 'legend': 'none',
  173. 'vAxis': {'title': 'Nombre de seums'},
  174. 'hAxis': {'title': 'Trigramme'},
  175. })
  176. # Graph of seum activity
  177. resets = Reset.objects.filter(
  178. timestamp__gte=timezone.now() - timedelta(days=365))
  179. months = {}
  180. for reset in resets:
  181. monthDate = datetime(reset.timestamp.year, reset.timestamp.month, 1)
  182. months[monthDate] = months.get(monthDate, 0) + 1
  183. monthList = sorted(months.items(), key=lambda t: t[0])
  184. seumActivity = []
  185. for month in monthList:
  186. seumActivity.append(
  187. [format_datetime(month[0], locale='fr',
  188. format="MMM Y").capitalize(), month[1]])
  189. if (len(seumActivity) == 0):
  190. noSeumActivity = True
  191. activity_chart = None
  192. else:
  193. noSeumActivity = False
  194. seumActivity.insert(0, ['Mois', 'Nombre de seums'])
  195. activity_data = SimpleDataSource(seumActivity)
  196. activity_chart = gchart.ColumnChart(activity_data, options={
  197. 'title': '',
  198. 'legend': 'none',
  199. 'vAxis': {'title': 'Nombre de seums'},
  200. 'hAxis': {'title': 'Mois'},
  201. })
  202. # Graph of best likers
  203. likersCounts = []
  204. for counter in counters:
  205. likersCounts.append(
  206. [counter.trigramme, Like.objects.filter(liker=counter).count()])
  207. if (len(likersCounts) == 0):
  208. noBestLikers = True
  209. likers_chart = None
  210. else:
  211. likersCounts.sort(key=lambda x: -x[1])
  212. noBestLikers = False
  213. likersCounts.insert(0, ['Trigramme', 'Nombre de likes distribués'])
  214. likers_data = SimpleDataSource(likersCounts[:bestSeumeursNumber])
  215. likers_chart = gchart.ColumnChart(likers_data, options={
  216. 'title': '',
  217. 'legend': 'none',
  218. 'vAxis': {'title': 'Nombre de likes distribués'},
  219. 'hAxis': {'title': 'Trigramme'},
  220. })
  221. # Graph of popular hashtags
  222. hashtagsCounts = []
  223. keywords = Keyword.objects.all()
  224. for keyword in keywords:
  225. hashtagsCounts.append(
  226. ['#' + keyword.text,
  227. Hashtag.objects.filter(keyword=keyword).count()])
  228. if (len(hashtagsCounts) == 0):
  229. noBestHashtags = True
  230. hashtags_chart = None
  231. else:
  232. hashtagsCounts.sort(key=lambda x: -x[1])
  233. noBestHashtags = False
  234. hashtagsCounts.insert(0, ['Trigramme', 'Nombre de likes distribués'])
  235. hashtags_data = SimpleDataSource(hashtagsCounts[:bestSeumeursNumber])
  236. hashtags_chart = gchart.ColumnChart(hashtags_data, options={
  237. 'title': '',
  238. 'legend': 'none',
  239. 'vAxis': {'title': 'Nombre de seums contenant le hashtag'},
  240. 'hAxis': {'title': 'Hashtag'},
  241. })
  242. # Graph of best likee
  243. likeesCounts = []
  244. for counter in counters:
  245. likeesCounts.append(
  246. [counter.trigramme,
  247. Like.objects.filter(reset__counter=counter).count()])
  248. if (len(likeesCounts) == 0):
  249. noBestLikees = True
  250. likees_chart = None
  251. else:
  252. likeesCounts.sort(key=lambda x: -x[1])
  253. noBestLikees = False
  254. likeesCounts.insert(0, ['Trigramme', 'Nombre de likes reçus'])
  255. likees_data = SimpleDataSource(likeesCounts[:bestSeumeursNumber])
  256. likees_chart = gchart.ColumnChart(likees_data, options={
  257. 'title': '',
  258. 'legend': 'none',
  259. 'vAxis': {'title': 'Nombre de likes reçus'},
  260. 'hAxis': {'title': 'Trigramme'},
  261. })
  262. # At last we render the page
  263. return render(request, 'homeTemplate.html', {
  264. 'counters': counters,
  265. 'line_chart': line_chart,
  266. 'best_chart': best_chart,
  267. 'likers_chart': likers_chart,
  268. 'likees_chart': likees_chart,
  269. 'hashtags_chart': hashtags_chart,
  270. 'activity_chart': activity_chart,
  271. 'noTimeline': noTimeline,
  272. 'noBestSeum': noBestSeum,
  273. 'noBestLikers': noBestLikers,
  274. 'noBestLikees': noBestLikees,
  275. 'noBestHashtags': noBestHashtags,
  276. 'noSeumActivity': noSeumActivity,
  277. 'myCounter': myCounter,
  278. })
  279. def createUser(request):
  280. if (request.method == 'POST'):
  281. # create a form instance and populate it with data from the request:
  282. data = dict(request.POST)
  283. email = data['email'][0]
  284. username = email.split('@')[0]
  285. trigramme = data['trigramme'][0]
  286. nick = data['nick'][0]
  287. password1 = data['password1'][0]
  288. password2 = data['password2'][0]
  289. email_notifications = ('email_notifications' in data.keys())
  290. if password1 != password2:
  291. error = "Les deux mots de passe sont différents."
  292. return render(request, 'createUser.html', {'error': error})
  293. try:
  294. test_user = User.objects.get(email=email)
  295. error = "Un utilisateur avec cette adresse email existe déjà !"
  296. return render(request, 'createUser.html', {'error': error})
  297. except User.DoesNotExist:
  298. try:
  299. user = User.objects.create_user(username, email, password1)
  300. except IntegrityError:
  301. error = "Utilise une autre adresse email, un autre utilisateur \
  302. a le même login que toi."
  303. return render(request, 'createUser.html', {'error': error})
  304. counter = Counter()
  305. counter.name = nick
  306. counter.email = email
  307. counter.trigramme = trigramme
  308. counter.user = user
  309. counter.email_notifications = False
  310. counter.save()
  311. return render(request, 'createUserDone.html', {'login': username})
  312. else:
  313. return render(request, 'createUser.html', {'error': None})
  314. @login_required
  315. def toggleEmailNotifications(request):
  316. counter = Counter.objects.get(user=request.user)
  317. counter.email_notifications = not counter.email_notifications
  318. counter.save()
  319. return HttpResponseRedirect(reverse('home'))
  320. @login_required
  321. def toggleScoreSorting(request):
  322. counter = Counter.objects.get(user=request.user)
  323. counter.sort_by_score = not counter.sort_by_score
  324. counter.save()
  325. return HttpResponseRedirect(reverse('home'))
  326. @login_required
  327. def like(request):
  328. if (request.method == 'POST'):
  329. # create a form instance and populate it with data from the request:
  330. data = dict(request.POST)
  331. liker = Counter.objects.get(pk=data['liker'][0])
  332. reset = Reset.objects.get(pk=data['reset'][0])
  333. like = Like()
  334. like.liker = liker
  335. like.reset = reset
  336. like.save()
  337. return HttpResponseRedirect(data['redirect'][0])