<commit_before># -*- coding: utf-8 -*-

"""
This file holds user profile information. (The base User model is part of
Django; profiles extend that with locally useful     information.)

TWLight has three user types:
* editors
* coordinators
* site administrators.

_Editors_ are Wikipedia editors who are applying for TWL resource access
grants. We track some of their data here to facilitate access grant
decisions.

_Coordinators_ are the Wikipedians who have responsibility for evaluating
and deciding on access grants. Site administrators should add editors to the
Coordinators group through the Django admin site.

_Site administrators_ have admin privileges for this site. They have no special
handling in this file; they are handled through the native Django is_admin
flag, and site administrators have responsibility for designating who has that
flag through the admin site.

New users who sign up via oauth will be created as editors. Site administrators
may promote them to coordinators manually in the Django admin site, by adding
them to the coordinators group. They can also directly create Django user
accounts without attached Editors in the admin site, if for some reason it's
useful to have account holders without attached Wikipedia data.
"""
from datetime import datetime, timedelta
import json
import logging
import urllib2

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _


logger = logging.getLogger(__name__)


class UserProfile(models.Model):
    """
    This is for storing data that relates only to accounts on TWLight, _not_ to
    Wikipedia accounts. All TWLight users have user profiles.
    """
    class Meta:
        app_label = 'users'
        # Translators: Gender unknown. This will probably only be displayed on admin-only pages.
        verbose_name = 'user profile'
        verbose_name_plural = 'user profiles'

    # Related name for backwards queries defaults to "userprofile".
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    # Have they agreed to our terms?
    terms_of_use = models.BooleanField(default=False,
        help_text=_("Has this user agreed with the terms of use?"))
    use_wp_email = models.BooleanField(default=True, help_text=_('Should we '
        'automatically update their email from their Wikipedia email when they '
        'log in? Defaults to True.'))


# Create user profiles automatically when users are created.
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

models.signals.post_save.connect(create_user_profile,
                                 sender=settings.AUTH_USER_MODEL)


class Editor(models.Model):
    """
    This model is for storing data related to people's accounts on Wikipedia.
    It is possible for users to have TWLight accounts and not have associated
    editors (if the account was created via manage.py createsuperuser),
    although some site functions will not be accessible.
    """
    class Meta:
        app_label = 'users'
        # Translators: Gender unknown. This will probably only be displayed on admin-only pages.
        verbose_name = 'wikipedia editor'
        verbose_name_plural = 'wikipedia editors'

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internal data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
    # Database recordkeeping.
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    last_updated = models.DateField(auto_now=True,
        help_text=_("When this information was last edited"))
    date_created = models.DateField(auto_now_add=True,
        help_text=_("When this profile was first created"))

    # ~~~~~~~~~~~~~~~~~~~~~~~ Data from Wikimedia OAuth ~~~~~~~~~~~~~~~~~~~~~~~#
    # Uses same field names as OAuth, but with wp_ prefixed.
    # Data are current *as of the time of TWLight signup* but may get out of
    # sync thereafter.
    wp_username = models.CharField(max_length=235,
        help_text=_("Username"))
    wp_editcount = models.IntegerField(help_text=_("Wikipedia edit count"))
    wp_registered = models.DateField(help_text=_("Date registered at Wikipedia"))
    wp_sub = models.IntegerField(unique=True,
        help_text=_("Wikipedia user ID")) # WP user id.

    # Should we want to filter these to check for specific group membership or
    # user rights in future:
    # Editor.objects.filter(wp_groups__icontains=groupname) or similar.
    wp_groups = models.TextField(help_text=_("Wikipedia groups"))
    wp_rights = models.TextField(help_text=_("Wikipedia user rights"))
    wp_valid = models.BooleanField(default=False,
        help_text=_('At their last login, did this user meet the criteria in '
        'the terms of use?'))


    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ User-entered data ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    contributions = models.TextField(
        help_text=_("Wiki contributions, as entered by user"),
        blank=True)

    # Fields we may, or may not, have collected in the course of applications
    # for resource grants.
    # **** SENSITIVE USER DATA AHOY. ****
    real_name = models.CharField(max_length=128, blank=True)
    country_of_residence = models.CharField(max_length=128, blank=True)
    occupation = models.CharField(max_length=128, blank=True)
    affiliation = models.CharField(max_length=128, blank=True)


    @cached_property
    def wp_user_page_url(self):
        url = u'{base_url}/User:{self.wp_username}'.format(
            base_url=settings.TWLIGHT_OAUTH_PROVIDER_URL, self=self)
        return url


    @cached_property
    def wp_talk_page_url(self):
        url = u'{base_url}/User_talk:{self.wp_username}'.format(
            base_url=settings.TWLIGHT_OAUTH_PROVIDER_URL, self=self)
        return url


    @cached_property
    def wp_email_page_url(self):
        url = u'{base_url}/Special:EmailUser/{self.wp_username}'.format(
            base_url=settings.TWLIGHT_OAUTH_PROVIDER_URL, self=self)
        return url


    @cached_property
    def wp_link_guc(self):
        url = u'{base_url}?user={self.wp_username}'.format(
            base_url='https://tools.wmflabs.org/guc/',
            self=self
        )
        return url


    @cached_property
    def wp_link_sul_info(self):
        url = u'{base_url}?username={self.wp_username}'.format(
            base_url='https://tools.wmflabs.org/quentinv57-tools/tools/sulinfo.php',
            self=self
        )
        return url


    @property
    def get_wp_rights_display(self):
        """
        This should be used to display wp_rights in a template, or any time
        we need to manipulate the rights as a list rather than a string.
        """
        return json.loads(self.wp_rights)


    @property
    def get_wp_groups_display(self):
        """
        As above, but for groups.
        """
        return json.loads(self.wp_groups)


    def _is_user_valid(self, identity, global_userinfo):
        """
        Check for the eligibility criteria laid out in the terms of service.<commit_msg>Don't blow up if we're missing some user data.<commit_after># -*- coding: utf-8 -*-

"""
This file holds user profile information. (The base User model is part of
Django; profiles extend that with locally useful     information.)

TWLight has three user types:
* editors
* coordinators
* site administrators.

_Editors_ are Wikipedia editors who are applying for TWL resource access
grants. We track some of their data here to facilitate access grant
decisions.

_Coordinators_ are the Wikipedians who have responsibility for evaluating
and deciding on access grants. Site administrators should add editors to the
Coordinators group through the Django admin site.

_Site administrators_ have admin privileges for this site. They have no special
handling in this file; they are handled through the native Django is_admin
flag, and site administrators have responsibility for designating who has that
flag through the admin site.

New users who sign up via oauth will be created as editors. Site administrators
may promote them to coordinators manually in the Django admin site, by adding
them to the coordinators group. They can also directly create Django user
accounts without attached Editors in the admin site, if for some reason it's
useful to have account holders without attached Wikipedia data.
"""
from datetime import datetime, timedelta
import json
import logging
import urllib2

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _


logger = logging.getLogger(__name__)


class UserProfile(models.Model):
    """
    This is for storing data that relates only to accounts on TWLight, _not_ to
    Wikipedia accounts. All TWLight users have user profiles.
    """
    class Meta:
        app_label = 'users'
        # Translators: Gender unknown. This will probably only be displayed on admin-only pages.
        verbose_name = 'user profile'
        verbose_name_plural = 'user profiles'

    # Related name for backwards queries defaults to "userprofile".
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    # Have they agreed to our terms?
    terms_of_use = models.BooleanField(default=False,
        help_text=_("Has this user agreed with the terms of use?"))
    use_wp_email = models.BooleanField(default=True, help_text=_('Should we '
        'automatically update their email from their Wikipedia email when they '
        'log in? Defaults to True.'))


# Create user profiles automatically when users are created.
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

models.signals.post_save.connect(create_user_profile,
                                 sender=settings.AUTH_USER_MODEL)


class Editor(models.Model):
    """
    This model is for storing data related to people's accounts on Wikipedia.
    It is possible for users to have TWLight accounts and not have associated
    editors (if the account was created via manage.py createsuperuser),
    although some site functions will not be accessible.
    """
    class Meta:
        app_label = 'users'
        # Translators: Gender unknown. This will probably only be displayed on admin-only pages.
        verbose_name = 'wikipedia editor'
        verbose_name_plural = 'wikipedia editors'

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internal data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
    # Database recordkeeping.
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    last_updated = models.DateField(auto_now=True,
        help_text=_("When this information was last edited"))
    date_created = models.DateField(auto_now_add=True,
        help_text=_("When this profile was first created"))

    # ~~~~~~~~~~~~~~~~~~~~~~~ Data from Wikimedia OAuth ~~~~~~~~~~~~~~~~~~~~~~~#
    # Uses same field names as OAuth, but with wp_ prefixed.
    # Data are current *as of the time of TWLight signup* but may get out of
    # sync thereafter.
    wp_username = models.CharField(max_length=235,
        help_text=_("Username"))
    wp_editcount = models.IntegerField(help_text=_("Wikipedia edit count"))
    wp_registered = models.DateField(help_text=_("Date registered at Wikipedia"))
    wp_sub = models.IntegerField(unique=True,
        help_text=_("Wikipedia user ID")) # WP user id.

    # Should we want to filter these to check for specific group membership or
    # user rights in future:
    # Editor.objects.filter(wp_groups__icontains=groupname) or similar.
    wp_groups = models.TextField(help_text=_("Wikipedia groups"))
    wp_rights = models.TextField(help_text=_("Wikipedia user rights"))
    wp_valid = models.BooleanField(default=False,
        help_text=_('At their last login, did this user meet the criteria in '
        'the terms of use?'))


    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ User-entered data ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    contributions = models.TextField(
        help_text=_("Wiki contributions, as entered by user"),
        blank=True)

    # Fields we may, or may not, have collected in the course of applications
    # for resource grants.
    # **** SENSITIVE USER DATA AHOY. ****
    real_name = models.CharField(max_length=128, blank=True)
    country_of_residence = models.CharField(max_length=128, blank=True)
    occupation = models.CharField(max_length=128, blank=True)
    affiliation = models.CharField(max_length=128, blank=True)


    @cached_property
    def wp_user_page_url(self):
        url = u'{base_url}/User:{self.wp_username}'.format(
            base_url=settings.TWLIGHT_OAUTH_PROVIDER_URL, self=self)
        return url


    @cached_property
    def wp_talk_page_url(self):
        url = u'{base_url}/User_talk:{self.wp_username}'.format(
            base_url=settings.TWLIGHT_OAUTH_PROVIDER_URL, self=self)
        return url


    @cached_property
    def wp_email_page_url(self):
        url = u'{base_url}/Special:EmailUser/{self.wp_username}'.format(
            base_url=settings.TWLIGHT_OAUTH_PROVIDER_URL, self=self)
        return url


    @cached_property
    def wp_link_guc(self):
        url = u'{base_url}?user={self.wp_username}'.format(
            base_url='https://tools.wmflabs.org/guc/',
            self=self
        )
        return url


    @cached_property
    def wp_link_sul_info(self):
        url = u'{base_url}?username={self.wp_username}'.format(
            base_url='https://tools.wmflabs.org/quentinv57-tools/tools/sulinfo.php',
            self=self
        )
        return url


    @property
    def get_wp_rights_display(self):
        """
        This should be used to display wp_rights in a template, or any time
        we need to manipulate the rights as a list rather than a string.
        Doesn't exist for batch loaded users.
        """
        if self.wp_groups:
            return json.loads(self.wp_rights)
        else:
            return None


    @property
    def get_wp_groups_display(self):
        """
        As above, but for groups.
        """
        if self.wp_groups:
            return json.loads(self.wp_groups)
        else:
            return None


    def _is_user_valid(self, identity, global_userinfo):
        """
        Check for the eligibility criteria laid out in the terms of service.
{'fraction_non_alphanumeric': 0.09558366162139748, 'fraction_numerical': 0.002626304513096966, 'mean_word_length': 3.3808658795034816, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2095472', 'n_tokens_mistral': 4110, 'n_tokens_neox': 3928, 'n_words': 1339}