text: import copy
  from datetime import datetime, timedelta
  import math
  
  from django import forms
  from django.contrib.auth.decorators import login_required
  from django.contrib.auth.models import User
  from django.core.mail import EmailMessage
  from django.core.urlresolvers import reverse
  from django.db import IntegrityError
  from django.db.models import Prefetch, Count
  from django.http import HttpResponseRedirect
  from django.shortcuts import render
  from django.template.loader import render_to_string
  from django.utils import timezone
  from django.utils.translation import ugettext as _, get_language
  
  import arrow
  from babel.dates import format_timedelta, format_datetime
  from graphos.renderers import gchart
  from graphos.sources.model import ModelDataSource
  from graphos.sources.simple import SimpleDataSource
  import numpy as np
  import pandas as pd
  
  from counter.models import *
  from counter.utils import parseSeumReason
  
  
  # Number of counters displayed on the home page's best seumeurs graph
  bestSeumeursNumber = 15
  
  
  @login_required
  def index(request):
      # Used later to keep track of the maximum JSS
      lastResets = []
      no_seum_delta = timedelta.max
  
      # First select our counter
      try:
          myCounter = Counter.objects.get(user__id=request.user.id)
          myLastReset = Reset.objects.select_related('who').filter(counter=myCounter).order_by('-timestamp').first()
  
          if myLastReset is None:
              # This person never had the seum
              myCounter.lastReset = Reset()
              myCounter.lastReset.delta = no_seum_delta
              myCounter.lastReset.formatted_delta = format_timedelta(myCounter.lastReset.delta, locale=get_language(), threshold=1)
              myCounter.lastReset.noSeum = True
          else:
              myCounter.lastReset = myLastReset
              myCounter.lastReset.noSeum = False
              if myCounter.lastReset.who is None or myCounter.lastReset.who.id == myCounter.id:
                  myCounter.lastReset.selfSeum = True
              else:
                  myCounter.lastReset.selfSeum = False
              likesMe = list(Like.objects.select_related('liker').filter(reset=myCounter.lastReset))
              myCounter.likeCount = len(likesMe)
              if myCounter.likeCount > 0:
                  myCounter.likersString = ", ".join([like.liker.trigramme for like in likesMe])
              myCounter.lastReset.formatted_delta = arrow.Arrow.fromdatetime(myCounter.lastReset.timestamp).humanize(locale=get_language())
  
      except Counter.DoesNotExist:
          return HttpResponseRedirect(reverse('login'))
  
      # Building data for counters display
      counters = Counter.objects.prefetch_related(
          'resets__likes',
          Prefetch(
              'resets',
              queryset=Reset.objects.prefetch_related('who', Prefetch('likes', queryset=Like.objects.select_related('liker'))).order_by('-timestamp'),
              to_attr='lastReset'
          )
      )
      for counter in counters:
          # Only the last reset is displayed
          lastReset = list(counter.lastReset)
          if len(lastReset) == 0:  # This person never had the seum
              counter.lastReset = Reset()
              counter.lastReset.delta = no_seum_delta
              counter.lastReset.formatted_delta = format_timedelta(counter.lastReset.delta, locale=get_language(), threshold=1)
              counter.lastReset.noSeum = True
              counter.lastReset.likes_count = -1
              counter.CSSclass = "warning"
          else:  # This person already had the seum
              counter.lastReset = lastReset[0]
              # To display the last seum we have to know if it is self-inflicted
              if counter.lastReset.who is None or counter.lastReset.who == counter:
                  counter.lastReset.selfSeum = True
              else:
                  counter.lastReset.selfSeum = False
              # Now we compute the duration since the reset
              counter.lastReset.noSeum = False
              counter.lastReset.delta = datetime.now() - counter.lastReset.timestamp.replace(tzinfo=None)
              # Defining CSS attributes for the counter
              counter.CSSclass = 'primary' if counter == myCounter else 'default'
              # Computing the total number of likes for this counter
              likesMe = list(counter.lastReset.likes.all())
              counter.lastReset.likes_count = len(likesMe)
              counter.alreadyLiked = myCounter.id in [likeMe.liker.id for likeMe in likesMe]
              if counter.lastReset.likes_count > 0:
                  counter.likersString = ", ".join([like.liker.trigramme for like in likesMe])
              counter.lastReset.formatted_delta = arrow.Arrow.fromdatetime(counter.lastReset.timestamp).humanize(locale=get_language())
  
          counter.likeCount = counter.lastReset.likes_count
          counter.isHidden = 'hidden'
  
      if myCounter.sort_by_score:
          # Now we sort the counters according to a reddit-like ranking formula
          # We take into account the number of likes of a reset and recentness
          # The log on the score will give increased value to the first likes
          # The counters with no seum have a like count of -1 by convention
          sorting_key = lambda t: - (math.log(t.lastReset.likes_count + 2) / (1 + (t.lastReset.delta.total_seconds()) / (24 * 3600)))
          counters = sorted(counters, key=sorting_key)
      else:
          counters = sorted(counters, key=lambda t: + t.lastReset.delta.total_seconds())
  
  
      # ### GRAPHS ###
      resets_raw = list(Reset.objects.select_related('who', 'counter').annotate(likes_count=Count('likes')))
      likes_raw = list(Like.objects.select_related('liker', 'reset__counter').all())
      hashtags_raw = list(Hashtag.objects.select_related('keyword').all())
      # Prepare pandas.DataFrames to efficiently process the data
      # About the counters
      resets_cols = ['date', 'counter', 'counter_trigram', 'who', 'who_trigram', 'reason', 'likes_count']
      resets_data = [[r.timestamp, r.counter.id, r.counter.trigramme, r.who, r.who, r.reason, r.likes_count] for r in resets_raw]
      for r in resets_data:
          r[3] = 0 if r[3] is None else r[3].id
          r[4] = '' if r[4] is None else r[4].trigramme
      resets_df = pd.DataFrame(resets_data, columns=resets_cols)
      resets_df['timestamp'] = resets_df.date.map(lambda d: d.timestamp())
      resets_df['self_seum'] = (resets_df.who.eq(np.zeros(resets_df.shape[0])) | resets_df.who.eq(resets_df.counter)).map(float)
      resets_df['formatted_delta'] = resets_df.date.map(lambda d: arrow.Arrow.fromdatetime(d).humanize(locale=get_language()))
      # About the likes
      likes_cols = ['liker', 'liker_trigram', 'counter', 'counter_trigram']
      likes_data = [[l.liker.id, l.liker.trigramme, l.reset.counter.id, l.reset.counter.trigramme] for l in likes_raw]
      likes_df = pd.DataFrame(likes_data, columns=likes_cols)
      # About the hashtags
      hashtags_cols = ['keyword']
      hashtags_data = [[h.keyword.text] for h in hashtags_raw]
      hashtags_df = pd.DataFrame(hashtags_data, columns=hashtags_cols)
  
  
      # Timeline graph
      timeline_resets = resets_df[resets_df.date > (datetime.now() - timedelta(days=1))].copy().reset_index()
      if timeline_resets.shape[0] == 0:
          noTimeline = True
          line_chart = None
      else:
          noTimeline = False
  
          # Construct legend for timeline dots
          legend_ = np.zeros(timeline_resets.shape[0], dtype=np.object)
          for i in range(timeline_resets.shape[0]):
              row = timeline_resets.iloc[i]
              if row['self_seum'] == 1:
                  legend_[i] = _('%(counter)s: %(reason)s') % {'counter': row['counter_trigram'], 'reason': row['reason']}
              else:
                  legend_[i] = _('%(who)s to %(counter)s: %(reason)s') % {'who': row['who_trigram'], 'counter': row['counter_trigram'], 'reason': row['reason']}
          timeline_resets['legend'] = legend_
  
          # Generate graph
          resets_ = [['', _('Seum')]]
          for i in range(timeline_resets.shape[0]):
              r = timeline_resets.iloc[i]
              resets_.append([{'v': r.timestamp, 'f': r.formatted_delta}, {'v': 0, 'f': r.legend}])
              # resets_.append({
              #     'timestamp': {'v': r.date.timestamp(), 'f': r.formatted_delta},
              #     'Seum': {'v': 0, 'f': r.legend},
              # })
          line_data = SimpleDataSource(resets_)
          line_chart = gchart.LineChart(line_data, options={
              'lineWidth': 0,
              'pointSize': 10,
              'title': '',
              'vAxis': {'ticks': []},
              'hAxis': {
                  'ticks': [
                      {'v': (datetime.now() - timedelta(days=1)
                             ).timestamp(), 'f': _('24h ago')},
                      {'v': datetime.now().timestamp(), 'f': _('Now')}
                  ]
              },
              'legend': 'none',
              'height': 90
          })
  
      # Graph of greatest seumers
      seum_counts_df = resets_df[['counter_trigram', 'self_seum']].copy()
      seum_counts_df['seum_count'] = np.ones(seum_counts_df.shape[0], dtype=np.float32)
      seum_counts_df = seum_counts_df.groupby(['counter_trigram']).sum().reset_index()
      # TODO: Add the ratio self_seum / seum_count
      if (seum_counts_df.shape[0] == 0):
          noBestSeum = True
          best_chart = None
      else:
          noBestSeum = False
          seum_counts_data = seum_counts_df.sort_values(by='seum_count', ascending=False)[['counter_trigram', 'seum_count']].values.tolist()
          seum_counts_data.insert(0, [_('Trigram'), _('Number of seums')])
          best_data = SimpleDataSource(seum_counts_data[:bestSeumeursNumber])
          best_chart = gchart.ColumnChart(best_data, options={
              'title': '',
              'legend': 'none',
              'vAxis': {'title': _('Number of seums')},
              'hAxis': {'title': _('Trigram')},
          })
  
      # Graph of seum activity
      resets_act = resets_df[resets_df.date > (timezone.now() - timedelta(days=365))][['date']].copy()
      resets_act['year'] = resets_df.date.map(lambda d: d.year)
      resets_act['month'] = resets_df.date.map(lambda d: d.month)
      resets_act = resets_act.drop(['date'], axis=1)
      resets_act['month_counts'] = np.ones(resets_act.shape[0], dtype=int)
      resets_act = resets_act.groupby(['year', 'month']).sum().reset_index()
      if resets_act.shape[0] == 0:
          noSeumActivity = True
          activity_chart = None
      else:
          noSeumActivity = False
          seumActivity = [
              [arrow.Arrow(a[0], a[1], 1).format("MMM YYYY", locale=get_language()).capitalize(), a[2]]
              for a in resets_act.values.tolist()
          ]
          seumActivity.insert(0, [_('Month'), _('Number of seums')])
          activity_data = SimpleDataSource(seumActivity)
          activity_chart = gchart.ColumnChart(activity_data, options={
              'title': '',
              'legend': 'none',
              'vAxis': {'title': _('Number of seums')},
              'hAxis': {'title': _('Month')},
          })
  
      # Graph of best likers
      best_likers_df = likes_df.drop(['liker', 'counter', 'counter_trigram'], axis=1)
      best_likers_df['count'] = np.ones(best_likers_df.shape[0], dtype=int)
      best_likers_df = best_likers_df.groupby(['liker_trigram']).sum().reset_index()
      if best_likers_df.shape[0] == 0:
          noBestLikers = True
          likers_chart = None
      else:
          noBestLikers = False
          likersCounts = best_likers_df.sort_values(by='count', ascending=False).values.tolist()
          likersCounts.insert(0, [_('Trigram'), _('Number of given likes')])
          likers_data = SimpleDataSource(likersCounts[:bestSeumeursNumber])
          likers_chart = gchart.ColumnChart(likers_data, options={
              'title': '',
              'legend': 'none',
              'vAxis': {'title': _('Number of given likes')},
              'hAxis': {'title': _('Trigram')},
          })
  
      # Graph of popular hashtags
      hashtags_df['count'] = np.ones(hashtags_df.shape[0], dtype=int)
      hashtags_df = hashtags_df.groupby(['keyword']).sum().reset_index()
      hashtags_df['keyword'] = hashtags_df.keyword.map(lambda x: '#' + x)
      if hashtags_df.shape[0] == 0:
          noBestHashtags = True
          hashtags_chart = None
      else:
          noBestHashtags = False
          hashtags_data = hashtags_df.sort_values(by='count', ascending=False).values.tolist()
          hashtags_data.insert(0, [_('Hashtag'), _('Number of seums containing the hashtag')])
          hashtags_data = SimpleDataSource(hashtags_data[:bestSeumeursNumber])
          hashtags_chart = gchart.ColumnChart(hashtags_data, options={
              'title': '',
              'legend': 'none',
              'vAxis': {'title': _('Number of seums containing the hashtag')},
              'hAxis': {'title': _('Hashtag')},
          })
  
      # Graph of best likee
      best_likees_df = likes_df.drop(['counter', 'liker', 'liker_trigram'], axis=1)
      best_likees_df['count'] = np.ones(best_likees_df.shape[0], dtype=int)
      best_likees_df = best_likees_df.groupby(['counter_trigram']).sum().reset_index()
      if best_likees_df.shape[0] == 0:
          noBestLikees = True
          likees_chart = None
      else:
          noBestLikees = False
          likeesCounts = best_likees_df.sort_values(by='count', ascending=False).values.tolist()
          likeesCounts.insert(0, [_('Trigram'), _('Number of received likes')])
          likees_data = SimpleDataSource(likeesCounts[:bestSeumeursNumber])
          likees_chart = gchart.ColumnChart(likees_data, options={
              'title': '',
              'legend': 'none',
              'vAxis': {'title': _('Number of received likes')},
              'hAxis': {'title': _('Trigram')},
          })
  
      # At last we render the page
      return render(request, 'homeTemplate.html', {
          'counters': counters,
          'line_chart': line_chart,
          'best_chart': best_chart,
          'likers_chart': likers_chart,
          'likees_chart': likees_chart,
          'hashtags_chart': hashtags_chart,
          'activity_chart': activity_chart,
          'noTimeline': noTimeline,
          'noBestSeum': noBestSeum,
          'noBestLikers': noBestLikers,
          'noBestLikees': noBestLikees,
          'noBestHashtags': noBestHashtags,
          'noSeumActivity': noSeumActivity,
          'myCounter': myCounter,
      })
  
  
  @login_required
  def toggleEmailNotifications(request):
      counter = Counter.objects.get(user=request.user)
      counter.email_notifications = not counter.email_notifications
      counter.save()
      return HttpResponseRedirect(reverse('home'))
  
  
  @login_required
  def toggleScoreSorting(request):
      counter = Counter.objects.get(user=request.user)
      counter.sort_by_score = not counter.sort_by_score
      counter.save()
      return HttpResponseRedirect(reverse('home'))
  