<commit_before>import logging

from datetime import datetime, timedelta

from google.appengine.ext import ndb
from google.appengine.api import mail

from protorpc import messages

from webapp2_extras import security
from webapp2_extras.appengine.auth.models import UserToken as BaseUserToken
from webapp2_extras.appengine.auth.models import User as BaseUser

from endpoints_proto_datastore.ndb import EndpointsModel
from endpoints_proto_datastore.ndb.properties import EndpointsComputedProperty

from . import config

# The UserToken and User classes here are derived from the respective classes
# provided by webapp2_extras:
# https://webapp-improved.appspot.com/_modules/webapp2_extras/appengine/auth
# /models.html

class UserToken( BaseUserToken ):
    # Need the user to be indexed
    user = ndb.StringProperty( required=True, indexed=True )

    @classmethod
    def count( cls, user, subject, limit, life_hours ):
        """ Counts the number of valid tokens of type subject assigned to user.

        :param user:
            The id of the user in question
        :param subject:
            The subject of the key
        :param limit:
            Maximum number of tokens to count
        :returns:
            Integer counting the number of such tokens
        """
        q = cls.query( cls.user == str( user ), cls.subject == subject )
        tokens = q.order( -cls.created ).fetch( limit,
                                                projection=[ cls.created ] )
        res = 0
        for token in tokens:
            # Check if token is still valid
            if( token.created + timedelta( hours=life_hours )
                <= datetime.now( ) ):
                # Token has expired
                return res
            res += 1

        return res

# Special UserToken class for verify email tokens as they need to store an
# additional email property
class VerifyEmailUserToken( UserToken ):
    SUBJECT = 'verify_email'
    
    email = ndb.StringProperty( required=True )

    @classmethod
    def create( cls, user, email ):
        """ Creates a new verify email token for the given user for validating
            the given email.

        :param user:
            User unique ID
        :param email:
            Email that the token validates
        :returns:
            The newly created :class:`VerifyEmailUserToken`.
        """
        user = str( user )
        token = security.generate_random_string( entropy=128 )
        key = cls.get_key( user, cls.SUBJECT, token )
        entity = cls( key=key, user=user, subject=cls.SUBJECT, token=token,
                      email=email.lower( ) )
        entity.put( )
        return entity

    @classmethod
    def count_by_email( cls, user, email ):
        """ Counts the number of valid tokens of type subject assigned to user
            for the given email.

        :param user:
            The id of the user in question
        :param email:
            The given email
        :returns:
            Integer counting the number of such valid tokens
        """
        q = cls.query( cls.user == str( user ), cls.subject == cls.SUBJECT,
                       cls.email == email.lower( ) )
        tokens = q.order( -cls.created ).fetch(
            config.MAX_VERIFY_EMAIL_TOKENS_PER_EMAIL,
            projection=[ cls.created ] )
        res = 0
        for token in tokens:
            # Check if token is still valid
            if( token.created + timedelta(
                    hours=config.TOKEN_LIFE_HOURS.get( 'verify_email' ) )
                <= datetime.now( ) ):
                # Token has expired
                return res
            res += 1

        return res
    

def lower_or_none( s ):
    if s:
        return s.lower( )
    else:
        return None

class User( BaseUser, EndpointsModel ):
    _message_fields_schema = ( 'email_verified', 'email_pending', 'username',
                               'has_password' )
    token_model = UserToken
    
    # Additional User properties
    email_verified = ndb.StringProperty( default=None, indexed=False )
    email_verified_lower = ndb.ComputedProperty(
        lambda self: lower_or_none( self.email_verified ) )
    email_pending = ndb.StringProperty( default=None, indexed=False )
    email_pending_lower = ndb.ComputedProperty(
        lambda self: lower_or_none( self.email_pending ) )
    username = ndb.StringProperty( required=True, indexed=False )
    username_lower = ndb.ComputedProperty(
        lambda self: self.username.lower( ) )
    is_mod = ndb.BooleanProperty( default=False, indexed=False )
    has_password = EndpointsComputedProperty(
        lambda self: self.password is not None,
        property_type=messages.BooleanField,
        indexed=False )
<commit_msg>Changed has_password from computed property to alias property<commit_after>import logging

from datetime import datetime, timedelta

from google.appengine.ext import ndb
from google.appengine.api import mail

from protorpc import messages

from webapp2_extras import security
from webapp2_extras.appengine.auth.models import UserToken as BaseUserToken
from webapp2_extras.appengine.auth.models import User as BaseUser

from endpoints_proto_datastore.ndb import EndpointsModel
from endpoints_proto_datastore.ndb.properties import EndpointsAliasProperty

from . import config

# The UserToken and User classes here are derived from the respective classes
# provided by webapp2_extras:
# https://webapp-improved.appspot.com/_modules/webapp2_extras/appengine/auth
# /models.html

class UserToken( BaseUserToken ):
    # Need the user to be indexed
    user = ndb.StringProperty( required=True, indexed=True )

    @classmethod
    def count( cls, user, subject, limit, life_hours ):
        """ Counts the number of valid tokens of type subject assigned to user.

        :param user:
            The id of the user in question
        :param subject:
            The subject of the key
        :param limit:
            Maximum number of tokens to count
        :returns:
            Integer counting the number of such tokens
        """
        q = cls.query( cls.user == str( user ), cls.subject == subject )
        tokens = q.order( -cls.created ).fetch( limit,
                                                projection=[ cls.created ] )
        res = 0
        for token in tokens:
            # Check if token is still valid
            if( token.created + timedelta( hours=life_hours )
                <= datetime.now( ) ):
                # Token has expired
                return res
            res += 1

        return res

# Special UserToken class for verify email tokens as they need to store an
# additional email property
class VerifyEmailUserToken( UserToken ):
    SUBJECT = 'verify_email'
    
    email = ndb.StringProperty( required=True )

    @classmethod
    def create( cls, user, email ):
        """ Creates a new verify email token for the given user for validating
            the given email.

        :param user:
            User unique ID
        :param email:
            Email that the token validates
        :returns:
            The newly created :class:`VerifyEmailUserToken`.
        """
        user = str( user )
        token = security.generate_random_string( entropy=128 )
        key = cls.get_key( user, cls.SUBJECT, token )
        entity = cls( key=key, user=user, subject=cls.SUBJECT, token=token,
                      email=email.lower( ) )
        entity.put( )
        return entity

    @classmethod
    def count_by_email( cls, user, email ):
        """ Counts the number of valid tokens of type subject assigned to user
            for the given email.

        :param user:
            The id of the user in question
        :param email:
            The given email
        :returns:
            Integer counting the number of such valid tokens
        """
        q = cls.query( cls.user == str( user ), cls.subject == cls.SUBJECT,
                       cls.email == email.lower( ) )
        tokens = q.order( -cls.created ).fetch(
            config.MAX_VERIFY_EMAIL_TOKENS_PER_EMAIL,
            projection=[ cls.created ] )
        res = 0
        for token in tokens:
            # Check if token is still valid
            if( token.created + timedelta(
                    hours=config.TOKEN_LIFE_HOURS.get( 'verify_email' ) )
                <= datetime.now( ) ):
                # Token has expired
                return res
            res += 1

        return res
    

def lower_or_none( s ):
    if s:
        return s.lower( )
    else:
        return None

class User( BaseUser, EndpointsModel ):
    _message_fields_schema = ( 'email_verified', 'email_pending', 'username',
                               'has_password' )
    token_model = UserToken
    
    # Additional User properties
    email_verified = ndb.StringProperty( default=None, indexed=False )
    email_verified_lower = ndb.ComputedProperty(
        lambda self: lower_or_none( self.email_verified ) )
    email_pending = ndb.StringProperty( default=None, indexed=False )
    email_pending_lower = ndb.ComputedProperty(
        lambda self: lower_or_none( self.email_pending ) )
    username = ndb.StringProperty( required=True, indexed=False )
    username_lower = ndb.ComputedProperty(
        lambda self: self.username.lower( ) )
    is_mod = ndb.BooleanProperty( default=False, indexed=False )

    @EndpointsAliasProperty( property_type=messages.BooleanField )
    def has_password( self ):
        return self.password is not None

{'fraction_non_alphanumeric': 0.0747144503824793, 'fraction_numerical': 0.002514932411191449, 'mean_word_length': 2.169711059448688, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7443733', 'n_tokens_mistral': 2583, 'n_tokens_neox': 2531, 'n_words': 819}