teams), 1):
                if (a_tm and p_tm) and (a_tm == p_tm):
                    good = pts_dct.get(i, 1)
                else:
                    good = 0

                points += good
                pck_res.append((good, teams[pck_tm] if pck_tm else None))
            results.append((points, -abs(adm.points - p.points), pck, pck_res))

        return [
            PlayoffResult(score=score, delta=delta, picks=picks, results=res)
            for score, delta, picks, res in sorted(results, reverse=True)
        ]

    #---------------------------------------------------------------------------
    @property
    def has_started(self):
        return self.kickoff < datetime_now()


#===============================================================================
class PlayoffTeam(models.Model):
    playoff = models.ForeignKey(Playoff)
    team = models.ForeignKey(Team)
    seed = models.PositiveSmallIntegerField()

    #===========================================================================
    class Meta:
        ordering = ('seed',)


#===============================================================================
class PlayoffPicks(models.Model):
    playoff = models.ForeignKey(Playoff)
    user    = models.ForeignKey(User, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    picks   = JSONField()

    #---------------------------------------------------------------------------
    def __unicode__(self):
        return unicode(self.user) if self.user else '<admin>'
    
    #---------------------------------------------------------------------------
    @cached_property
    def season(self):
        return self.playoff.season
    
    #---------------------------------------------------------------------------
    @property
    def teams(self):
        return tuple([self.picks.get('game_%d' % i) for i in range(1, 12)])
    
    #---------------------------------------------------------------------------
    @property
    def teams_by_round(self):
        teams = self.teams
        return tuple([teams[:4], teams[4:8], teams[8:10], teams[10:]])
    
    #---------------------------------------------------------------------------
    @property
    def points(self):
        pts = self.picks.get('points', '')
        return int(pts) if pts.isdigit() else 0


#===============================================================================
class RosterStats(object):
    
    #---------------------------------------------------------------------------
    def __init__(self, preference, league, season=None):
        self.preference = preference
        self.user = preference.user
        self.season = season
        self.league = league
        self.is_active = preference.is_active
        self.correct = 0
        self.wrong = 0
        self.points_delta = 0

        qs = self.user.pick_set.select_related().filter(
            models.Q(correct__gt=0) | models.Q(wrong__gt=0)
        )
        
        if season:
            qs = qs.filter(week__season=season)
            
        self.weeks_played = len(qs)
        for wp in qs:
            self.correct += wp.correct
            self.wrong += wp.wrong
            self.points_delta += wp.points_delta if wp.week.points else 0

    #---------------------------------------------------------------------------
    @property
    def weeks_won(self):
        qs = GameSet.objects.filter(weeksummary__weekwinner__user=self.user)
        if self.season:
            qs = qs.filter(season=self.season)
            
        return list(qs.select_related())
        
    #---------------------------------------------------------------------------
    def __str__(self):
        return '%s' % self.user
    
    __repr__ = __str__
    
    #---------------------------------------------------------------------------
    @property
    def pct(self):
        return utils.percent(self.correct, self.correct + self.wrong)
    
    #---------------------------------------------------------------------------
    @property
    def avg_points_delta(self):
        if not self.weeks_played:
            return 0
            
        return float(self.points_delta) / self.weeks_played
        
    #---------------------------------------------------------------------------
    def __cmp__(self, other):
        return (
            -cmp(self.correct, other.correct)           or
             cmp(self.points_delta, other.points_delta) or
            -cmp(self.weeks_played, other.weeks_played)
        )

    #---------------------------------------------------------------------------
    @staticmethod
    def get_details(league, season=None):
        season = season or league.current_season
        prefs = Preference.objects.select_related().order_by('user__username')
        
        by_user = {
            entry[1].user: entry
            for entry in sorted_standings([RosterStats(p, league) for p in prefs])
        }

        return [
            entry + by_user[entry[1].user]
            for entry in sorted_standings([RosterStats(p, league, season) for p in prefs])
        ]


#-------------------------------------------------------------------------------
def sorted_standings(items):
    weighted = []
    prev_place, prev_results = 1, (0,0)
    for i, item in enumerate(sorted(items), 1): 
        results = (item.correct, item.points_delta)
        place = prev_place if results == prev_results else i
        prev_place, prev_results = place, results
        weighted.append((place, item))
        
    return weighted


_participation_hooks = None

#-------------------------------------------------------------------------------
def can_user_participate(pref, week):
    global _participation_hooks
    if _participation_hooks is None:
        hooks = picker_setting('PARTICIPATION_HOOKS', [])
        _participation_hooks = [import_string(hook) for hook in hooks]
    
    for hook in _participation_hooks:
        if not hook(pref, week):
            return False
    
    return True<commit_msg>A few model additions<commit_after># -*- coding:utf8 -*-
import re
import json
import random
from importlib import import_module
from decimal import Decimal
from datetime import datetime, timedelta, time
from dateutil import relativedelta as rd
from dateutil.parser import parse

from django.db import models
from django.conf import settings
from django.utils.functional import cached_property
from django.contrib.auth.models import User
from django.utils.module_loading import import_string

from choice_enum import ChoiceEnumeration
from django_extensions.db.fields.json import JSONField

from. import utils
from. import signals
from.conf import get_setting as picker_setting
from. import managers


GAME_DURATION  = timedelta(hours=4.5)

datetime_now = utils.datetime_now

#===============================================================================
class EnumField(models.CharField):
    
    #---------------------------------------------------------------------------
    def __init__(self, enum, *args, **kws):
        kws.update(choices=enum.CHOICES, default=enum.DEFAULT)
        super(EnumField, self).__init__(*args, **kws)


#===============================================================================
class Preference(models.Model):
    
    #===========================================================================
    class Status(ChoiceEnumeration):
        ACTIVE    = ChoiceEnumeration.Option('ACTV', 'Active', default=True)
        INACTIVE  = ChoiceEnumeration.Option('IDLE', 'Inactive')
        SUSPENDED = ChoiceEnumeration.Option('SUSP', 'Suspended')
    
    #===========================================================================
    class Autopick(ChoiceEnumeration):
        NONE        = ChoiceEnumeration.Option('NONE', 'None')
        RANDOM      = ChoiceEnumeration.Option('RAND', 'Random', default=True)
        
    league        = models.ForeignKey('League')
    favorite_team = models.ForeignKey('Team', null=True, blank=True)
    user          = models.OneToOneField(User, related_name='picker_preferences')
    status        = EnumField(Status, max_length=4)
    autopick      = EnumField(Autopick, max_length=4)

    objects = managers.PreferenceManager()
    
    #---------------------------------------------------------------------------
    def __unicode__(self):
        return self.user.username
    
    #---------------------------------------------------------------------------
    @cached_property
    def email(self):
        return self.user.