from __future__ import absolute_import, unicode_literals

from dash.utils import get_obj_cacheable
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from tracpro.groups.models import Region


# === Monkey patching for the User class === #

def _user_create(cls, org, full_name, email, password, change_password=False, regions=()):
    """
    Creates a regular user with specific region access
    """
    # create auth user
    user = cls.objects.create(is_active=True, username=email, email=email)
    user.set_password(password)
    user.save()

    # add profile
    from .models import Profile
    Profile.objects.create(user=user, full_name=full_name, change_password=change_password)

    # setup as org editor with limited region access
    if org:
        user.org_editors.add(org)
    if regions:
        user.update_regions(regions)
    return user


def _user_clean(user):
    # we use email for login
    if User.objects.filter(email=user.email).exclude(pk=user.pk).exists():
        raise ValidationError(_("Email address already taken."))

    user.username = user.email

    super(User, user).clean()


def _user_has_profile(user):
    from .models import Profile
    try:
        return bool(user.profile)
    except Profile.DoesNotExist:
        return False


def _user_get_full_name(user):
    """
    Override regular get_full_name which returns first_name + last_name
    """
    return user.profile.full_name if user.has_profile() else " ".join([user.first_name, user.last_name]).strip()


def _user_get_regions(user, org):
    def calculate():
        # org admins have implicit access to all regions
        if user.is_admin_for(org):
            return Region.get_all(org)
        else:
            return user.regions.filter(is_active=True)

    return get_obj_cacheable(user, '_regions', calculate)


def _user_update_regions(user, regions):
    """
    Updates a user's regions
    """
    user.regions.clear()
    user.regions.add(*regions)


def _user_has_region_access(user, region):
    """
    Whether the given user has access to the given region
    """
    if user.is_superuser or user.is_admin_for(region.org):
        return True
    else:
        return user.regions.filter(pk=region.pk).exists()


def _user_is_admin_for(user, org):
    """
    Whether this user is an administrator for the given org
    """
    return org.administrators.filter(pk=user.pk).exists()


def _user_unicode(user):
    if user.has_profile():
        if user.profile.full_name:
            return user.profile.full_name
    else:
        return user.username  # superuser

    return user.email or user.username


User.create = classmethod(_user_create)
User.clean = _user_clean
User.has_profile = _user_has_profile
User.get_full_name = _user_get_full_name
User.get_regions = _user_get_regions
User.update_regions = _user_update_regions
User.has_region_access = _user_has_region_access
User.is_admin_for = _user_is_admin_for
User.__unicode__ = User.__str__ = _user_unicode

@@ -92,16 +92,17 @@
 cheable%0A
+%0A
 from dja
@@ -244,16 +244,17 @@
 zy as _%0A
+%0A
 from tra
@@ -287,16 +287,45 @@
 Region%0A%0A
+from .models import Profile%0A%0A
 %0A# === M
@@ -442,16 +442,33 @@
 d=False,
+%0A                
  regions
@@ -713,40 +713,8 @@
 ile%0A
-    from .models import Profile%0A
     
@@ -1164,17 +1164,16 @@
 ken.%22))%0A
-%0A
     user
@@ -1195,17 +1195,16 @@
 r.email%0A
-%0A
     supe
@@ -1260,40 +1260,8 @@
 r):%0A
-    from .models import Profile%0A
     
@@ -1466,32 +1466,63 @@
 st_name%0A    %22%22%22%0A
+    if user.has_profile():%0A    
     return user.
@@ -1542,85 +1542,51 @@
 name
- if user.has_profile() else %22 %22.join(%5Buser.first_name, user.last_name%5D).strip
+%0A    return super(User, user).get_full_name
 ()%0A%0A
@@ -1841,17 +1841,16 @@
 e=True)%0A
-%0A
     retu
@@ -2714,16 +2714,30 @@
 ser.
+add_to_class('
 create
- =
+',
  cla
@@ -2762,21 +2762,36 @@
 ate)
+)
 %0AUser.
+add_to_class('
 clean
- =
+',
  _us
@@ -2798,22 +2798,37 @@
 er_clean
+)
 %0AUser.
+add_to_class('
 has_prof
@@ -2830,18 +2830,18 @@
 _profile
- =
+',
  _user_h
@@ -2850,22 +2850,37 @@
 _profile
+)
 %0AUser.
+add_to_class('
 get_full
@@ -2884,18 +2884,18 @@
 ull_name
- =
+',
  _user_g
@@ -2906,22 +2906,37 @@
 ull_name
+)
 %0AUser.
+add_to_class('
 get_regi
@@ -2934,26 +2934,26 @@
 'get_regions
- =
+',
  _user_get_r
@@ -2958,22 +2958,37 @@
 _regions
+)
 %0AUser.
+add_to_class('
 update_r
@@ -2993,18 +2993,18 @@
 _regions
- =
+',
  _user_u
@@ -3016,22 +3016,37 @@
 _regions
+)
 %0AUser.
+add_to_class('
 has_regi
@@ -3054,18 +3054,18 @@
 n_access
- =
+',
  _user_h
@@ -3080,22 +3080,37 @@
 n_access
+)
 %0AUser.
+add_to_class('
 is_admin
@@ -3113,18 +3113,18 @@
 dmin_for
- =
+',
  _user_i
@@ -3138,42 +3138,86 @@
 _for
+)
 %0AUser.
-__unicode__ = User.
+add_to_class('__unicode__', _user_unicode)%0AUser.add_to_class('
 __str__
- =
+',
  _us
@@ -3226,9 +3226,10 @@
 _unicode
+)
 %0A

Use add_to_class to monkey-patch User model