<commit_before>import calendar
import datetime
import gzip
import operator
import re
from collections import OrderedDict

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, EmptyPage, InvalidPage, PageNotAnInteger
from django.core.urlresolvers import reverse
from django.db import connection
from django.db.models import Q, Avg, Count, Max, Min, Sum
from django.http import Http404
from django.shortcuts import redirect, get_object_or_404
from django.template import RequestContext
from django.views.decorators.debug import sensitive_post_parameters, sensitive_variables

from coffin.shortcuts import *

from thing import queries
from thing.forms import *
from thing.models import *
from thing.stuff import *
from thing.templatetags.thing_extras import commas, duration, shortduration

# ---------------------------------------------------------------------------
# How many days (10) to start warning about expiring accounts
ONE_DAY = 24 * 60 * 60
EXPIRE_WARNING = 10 * ONE_DAY

ORDER_SLOT_SKILLS = {
    'Trade': 4,
    'Retail': 8,
    'Wholesale': 16,
    'Tycoon': 32,
}

TWO_PLACES = Decimal('0.00')

# ---------------------------------------------------------------------------
# Home page
@login_required
def home(request):
    # Create the user's profile if it doesn't already exist
    try:
        profile = request.user.get_profile()
    except UserProfile.DoesNotExist:
        profile = UserProfile(user=request.user)
        profile.save()

    now = datetime.datetime.utcnow()
    total_balance = 0

    # Make a set of characters to hide
    hide_characters = set(int(c) for c in profile.home_hide_characters.split(',') if c)

    # Initialise various data structures
    api_keys = set()
    training = set()
    chars = {}
    for apikey in APIKey.objects.prefetch_related('characters').filter(user=request.user).exclude(key_type=APIKey.CORPORATION_TYPE):
        api_keys.add(apikey)
        for char in apikey.characters.all():
            chars[char.id] = char
            char.z_apikey = apikey
            char.z_training = {}
            total_balance += char.wallet_balance

    # Do skill training check - this can't be in the model because it
    # scales like crap doing individual queries
    utcnow = datetime.datetime.utcnow()
    queues = SkillQueue.objects.select_related('character', 'skill__item').filter(character__in=chars, end_time__gte=utcnow)
    for sq in queues:
        char = chars[sq.character_id]
        if 'sq' not in char.z_training:
            char.z_training['sq'] = sq
            char.z_training['skill_duration'] = (sq.end_time - utcnow).total_seconds()
            char.z_training['sp_per_hour'] = int(sq.skill.get_sp_per_minute(char) * 60)
            char.z_training['complete_per'] = sq.get_complete_percentage(now)
            training.add(char.z_apikey)
        
        char.z_training['queue_duration'] = (sq.end_time - utcnow).total_seconds()

    # Do total skill point aggregation
    for cs in CharacterSkill.objects.select_related().filter(character__in=chars).values('character').annotate(total_sp=Sum('points')):
        chars[cs['character']].z_total_sp = cs['total_sp']

    # Work out who is and isn't training
    not_training = api_keys - training

    # Do notifications
    for char_id, char in chars.items():
        char.z_notifications = []

        # Game time warnings
        if char.z_apikey.paid_until:
            timediff = (char.z_apikey.paid_until - now).total_seconds()

            if timediff < 0:
                char.z_notifications.append({
                    'icon': 'time-warning',
                    'text': 'Expired',
                    'tooltip': 'Game time has expired!',
                    'span_class': 'low-game-time',
                })

            elif timediff < EXPIRE_WARNING:
                char.z_notifications.append({
                    'icon': 'time-warning',
                    'text': shortduration(timediff),
                    'tooltip': 'Remaining game time is low!',
                    'span_class': 'low-game-time',
                })

        # Empty skill queue
        if char.z_apikey in not_training:
            char.z_notifications.append({
                'icon': 'queue-empty',
                'text': 'Empty!',
                'tooltip': 'Skill queue is empty!',
            })
        
        if char.z_training:
            # Room in skill queue
            if char.z_training['queue_duration'] < ONE_DAY:
                timediff = ONE_DAY - char.z_training['queue_duration']
                char.z_notifications.append({
                    'icon': 'queue-space',
                    'text': shortduration(timediff),
                    'tooltip': 'Skill queue is not full!',
                })

            # Missing implants
            skill = char.z_training['sq'].skill
            pri_attrs = Skill.ATTRIBUTE_MAP[skill.primary_attribute]
            sec_attrs = Skill.ATTRIBUTE_MAP[skill.secondary_attribute]
            pri_bonus = getattr(char, pri_attrs[1])
            sec_bonus = getattr(char, sec_attrs[1])

            if pri_bonus == 0 or sec_bonus == 0:
                t = []
                if pri_bonus == 0:
                    t.append(skill.get_primary_attribute_display())
                if sec_bonus == 0:
                    t.append(skill.get_secondary_attribute_display())

                char.z_notifications.append({
                    'icon': 'missing-implants',
                    'text': ', '.join(t),
                    'tooltip': 'Missing stat implants for currently training skill!',
                })

        # Insufficient clone
        if hasattr(char, 'z_total_sp') and char.z_total_sp > char.clone_skill_points:
            char.z_notifications.append({
                'icon': 'inadequate-clone',
                'text': '%s SP' % (commas(char.clone_skill_points)),
                'tooltip': 'Insufficient clone!',
            })

    # Total SP
    total_sp = sum(getattr(c, 'z_total_sp', 0) for c in chars.values())

    # Work out sort order
    char_list = chars.values()
    if profile.home_sort_order == 'apiname':
        temp = [(c.z_apikey.name, c.name.lower(), c) for c in char_list]
    elif profile.home_sort_order == 'charname':
        temp = [(c.name.lower(), c) for c in char_list]
    elif profile.home_sort_order == 'corpname':
        temp = [(c.corporation.name.lower(), c.name.lower(), c) for c in char_list]
    elif profile.home_sort_order == 'totalsp':
        temp = [(getattr(c, 'z_total_sp', 0), c) for c in char_list]
    elif profile.home_sort_order == 'wallet':
        temp = [(c.wallet_balance, c.name.lower(), c) for c in char_list]

    temp.sort()
    if profile.home_sort_descending:
        temp.reverse()

    char_list = [s[-1] for s in temp]
    

    # Make separate lists of training and not training characters
    first = [char for char in char_list if char.z_training and char.id not in hide_characters]
    last = [char for char in char_list if not char.z_training and char.id not in hide_characters]

    # Get corporations this user has APIKeys for
    corp_ids = APIKey.objects.select_related().filter(user=request.user.id).exclude(corp_character=None).values_list('corp_character__corporation', flat=True)
    corporations = Corporation.objects.filter(pk__in=corp_ids)

    # Get old event stats for staff users
    if request.user.is_staff:
        task_count = TaskState.objects.filter(state=TaskState.QUEUED_STATE).aggregate(Count('id'))['id__count']
    else:
        task_count = 0

    return render_to_response(
        'thing/home.html',
        {
            'profile': profile,
            'not_training': not_training,
            'total_balance': total_balance,
            'total_sp': total_sp,
            'corporations': corporations,
            'characters': first + last,
            'events': Event.objects.filter(user=request.user)[:10],
            'task_count': task_count,
        },
        context_instance=RequestContext(request)
    )

# ---------------------------------------------------------------------------
# Various account stuff
@login_required
def account(request):
    if 'message' in request.session:
        message = request.session.pop('message')
        message_type = request.session.pop('message_type')
    else:
        message = None
        message_type = None

    profile = request.user.get_profile()

    characters = Character.objects.filter(apikeys__user=request.user).distinct()
    home_hide_characters = set(int(c) for c in profile.home_hide_characters.split(',') if c)
<commit_msg>Add timing code to home view<commit_after>import calendar
import datetime
import gzip
import operator
import re
from collections import OrderedDict

from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, EmptyPage, InvalidPage, PageNotAnInteger
from django.core.urlresolvers import reverse
from django.db import connection
from django.db.models import Q, Avg, Count, Max, Min, Sum
from django.http import Http404
from django.shortcuts import redirect, get_object_or_404
from django.template import RequestContext
from django.views.decorators.debug import sensitive_post_parameters, sensitive_variables

from coffin.shortcuts import *

from thing import queries
from thing.forms import *
from thing.models import *
from thing.stuff import *
from thing.templatetags.thing_extras import commas, duration, shortduration

# ---------------------------------------------------------------------------
# How many days (10) to start warning about expiring accounts
ONE_DAY = 24 * 60 * 60
EXPIRE_WARNING = 10 * ONE_DAY

ORDER_SLOT_SKILLS = {
    'Trade': 4,
    'Retail': 8,
    'Wholesale': 16,
    'Tycoon': 32,
}

TWO_PLACES = Decimal('0.00')

# ---------------------------------------------------------------------------
# Home page
@login_required
def home(request):
    tt = TimerThing()

    # Create the user's profile if it doesn't already exist
    try:
        profile = request.user.get_profile()
    except UserProfile.DoesNotExist:
        profile = UserProfile(user=request.user)
        profile.save()

    tt.add_time('profile')

    now = datetime.datetime.utcnow()
    total_balance = 0

    # Make a set of characters to hide
    hide_characters = set(int(c) for c in profile.home_hide_characters.split(',') if c)

    # Initialise various data structures
    api_keys = set()
    training = set()
    chars = {}

    for apikey in APIKey.objects.prefetch_related('characters').filter(user=request.user).exclude(key_type=APIKey.CORPORATION_TYPE):
        api_keys.add(apikey)
        for char in apikey.characters.all():
            chars[char.id] = char
            char.z_apikey = apikey
            char.z_training = {}
            total_balance += char.wallet_balance

    tt.add_time('apikeys')

    # Do skill training check - this can't be in the model because it
    # scales like crap doing individual queries
    utcnow = datetime.datetime.utcnow()
    queues = SkillQueue.objects.select_related('character', 'skill__item').filter(character__in=chars, end_time__gte=utcnow)
    for sq in queues:
        char = chars[sq.character_id]
        if 'sq' not in char.z_training:
            char.z_training['sq'] = sq
            char.z_training['skill_duration'] = (sq.end_time - utcnow).total_seconds()
            char.z_training['sp_per_hour'] = int(sq.skill.get_sp_per_minute(char) * 60)
            char.z_training['complete_per'] = sq.get_complete_percentage(now)
            training.add(char.z_apikey)
        
        char.z_training['queue_duration'] = (sq.end_time - utcnow).total_seconds()

    tt.add_time('training')

    # Do total skill point aggregation
    total_sp = 0
    for cs in CharacterSkill.objects.select_related().filter(character__in=chars).values('character').annotate(total_sp=Sum('points')):
        chars[cs['character']].z_total_sp = cs['total_sp']
        total_sp += cs['total_sp']

    tt.add_time('total_sp')

    # Work out who is and isn't training
    not_training = api_keys - training

    # Do notifications
    for char_id, char in chars.items():
        char.z_notifications = []

        # Game time warnings
        if char.z_apikey.paid_until:
            timediff = (char.z_apikey.paid_until - now).total_seconds()

            if timediff < 0:
                char.z_notifications.append({
                    'icon': 'time-warning',
                    'text': 'Expired',
                    'tooltip': 'Game time has expired!',
                    'span_class': 'low-game-time',
                })

            elif timediff < EXPIRE_WARNING:
                char.z_notifications.append({
                    'icon': 'time-warning',
                    'text': shortduration(timediff),
                    'tooltip': 'Remaining game time is low!',
                    'span_class': 'low-game-time',
                })

        # Empty skill queue
        if char.z_apikey in not_training:
            char.z_notifications.append({
                'icon': 'queue-empty',
                'text': 'Empty!',
                'tooltip': 'Skill queue is empty!',
            })
        
        if char.z_training:
            # Room in skill queue
            if char.z_training['queue_duration'] < ONE_DAY:
                timediff = ONE_DAY - char.z_training['queue_duration']
                char.z_notifications.append({
                    'icon': 'queue-space',
                    'text': shortduration(timediff),
                    'tooltip': 'Skill queue is not full!',
                })

            # Missing implants
            skill = char.z_training['sq'].skill
            pri_attrs = Skill.ATTRIBUTE_MAP[skill.primary_attribute]
            sec_attrs = Skill.ATTRIBUTE_MAP[skill.secondary_attribute]
            pri_bonus = getattr(char, pri_attrs[1])
            sec_bonus = getattr(char, sec_attrs[1])

            if pri_bonus == 0 or sec_bonus == 0:
                t = []
                if pri_bonus == 0:
                    t.append(skill.get_primary_attribute_display())
                if sec_bonus == 0:
                    t.append(skill.get_secondary_attribute_display())

                char.z_notifications.append({
                    'icon': 'missing-implants',
                    'text': ', '.join(t),
                    'tooltip': 'Missing stat implants for currently training skill!',
                })

        # Insufficient clone
        if hasattr(char, 'z_total_sp') and char.z_total_sp > char.clone_skill_points:
            char.z_notifications.append({
                'icon': 'inadequate-clone',
                'text': '%s SP' % (commas(char.clone_skill_points)),
                'tooltip': 'Insufficient clone!',
            })

    tt.add_time('notifications')

    # Work out sort order
    char_list = chars.values()
    if profile.home_sort_order == 'apiname':
        temp = [(c.z_apikey.name, c.name.lower(), c) for c in char_list]
    elif profile.home_sort_order == 'charname':
        temp = [(c.name.lower(), c) for c in char_list]
    elif profile.home_sort_order == 'corpname':
        temp = [(c.corporation.name.lower(), c.name.lower(), c) for c in char_list]
    elif profile.home_sort_order == 'totalsp':
        temp = [(getattr(c, 'z_total_sp', 0), c) for c in char_list]
    elif profile.home_sort_order == 'wallet':
        temp = [(c.wallet_balance, c.name.lower(), c) for c in char_list]

    temp.sort()
    if profile.home_sort_descending:
        temp.reverse()

    char_list = [s[-1] for s in temp]
    
    tt.add_time('sort')

    # Make separate lists of training and not training characters
    first = [char for char in char_list if char.z_training and char.id not in hide_characters]
    last = [char for char in char_list if not char.z_training and char.id not in hide_characters]

    # Get corporations this user has APIKeys for
    corp_ids = APIKey.objects.select_related().filter(user=request.user.id).exclude(corp_character=None).values_list('corp_character__corporation', flat=True)
    corporations = Corporation.objects.filter(pk__in=corp_ids)

    # Get old event stats for staff users
    if request.user.is_staff:
        task_count = TaskState.objects.filter(state=TaskState.QUEUED_STATE).aggregate(Count('id'))['id__count']
    else:
        task_count = 0

    tt.add_time('misc junk')

    out = render_to_response(
        'thing/home.html',
        {
            'profile': profile,
            'not_training': not_training,
            'total_balance': total_balance,
            'total_sp': total_sp,
            'corporations': corporations,
            'characters': first + last,
            'events': Event.objects.filter(user=request.user)[:10],
            'task_count': task_count,
        },
        context_instance=RequestContext(request)
    )

    tt.add_time('template')
    if settings.DEBUG:
        tt.finished()

    return out

# ---------------------------------------------------------------------------
# Various account stuff
@login_required
def account(request):
    if 'message' in request.session:
        message = request.session.pop('message')
        message_type = request.session.pop('message_type')
    else:
        message = None
        message_type = None

    profile = request.user.get_profile()

    characters = Character.objects.filter(apikeys__user=request.user).distinct()
    home_hide_characters = set(int(c) for c in profile.home_hide_characters.split(',') if c)

{'fraction_non_alphanumeric': 0.1288194835945428, 'fraction_numerical': 0.004622843612583155, 'mean_word_length': 2.2500916086478564, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 2, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5166684', 'n_tokens_mistral': 5291, 'n_tokens_neox': 5058, 'n_words': 1207}