<commit_before># -*- coding: utf-8 -*-
# Copyright 2016 Open Permissions Platform Coalition
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed under the License is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
from __future__ import unicode_literals

import uuid
from datetime import datetime, timedelta
from enum import Enum

import couch
from dateutil import parser
from passlib.hash import pbkdf2_sha256
from tornado.gen import coroutine, Return
from tornado.options import options, define
from voluptuous import All, Extra, In, Length, Required, Schema

from . import views, exceptions, validators
from .model import Document, SubResource, State


__all__ = ['User', 'UserOrganisation', 'Token']

define('min_length_password', default=3)
define('max_length_password', default=128)
define('token_ttl', default=86400)


class User(Document):
    resource_type = 'user'
    view = views.users
    active_view = views.active_users
    db_name = 'registry'
    internal_fields = (Document.internal_fields +
                       ['password', 'verification_hash', 'pre_verified'])
    read_only_fields = ['created_by']

    default_state = State.approved
    editable_states = [State.approved]

    # State transitions for users overridden so that:
    # - Only valid states are approved and deactivated
    # - Once user is deactivated cannot be reactivated
    approval_state_transitions = {}

    state_transitions = {
        None: [State.approved.name],
        State.approved.name: [State.deactivated.name]
    }

    class roles(Enum):
        administrator = 'administrator'
        user = 'user'
        reseller = 'reseller'
        default = user

    @property
    def schema(cls):
        orgs_schema = Schema({
            Extra: {
                'state': validators.validate_state,
                'role': In([x.value for x in cls.roles])
            }
        }, required=True)

        password_length = Length(min=options.min_length_password,
                                 max=options.max_length_password)

        schema = Schema({
            '_id': unicode,
            '_rev': unicode,
            'doc_version': unicode,
            Required('type', default=cls.resource_type): cls.resource_type,
            Required('email'): validators.valid_email,
            Required('password'): All(unicode, password_length),
            Required('has_agreed_to_terms'): True,
            Required('organisations', default={}): orgs_schema,
            Required('state', default=User.default_state.name): validators.validate_user_state,
            Required('role', default=User.roles.default.value): In([x.value for x in User.roles]),
            'first_name': unicode,
            'last_name': unicode,
            'phone': unicode,
            'verification_hash': unicode,
            'pre_verified': bool, 
            'actor': unicode  # only used in fixtures, should it be removed?
        })

        return schema

    def clean(self):
        """Verified value is derived from whether user has a verification hash"""
        result = super(User, self).clean()
        result['verified'] = 'verification_hash' not in self._resource
        return result

    @coroutine
    def can_update(self, user, **kwargs):
        # Sys admin can update everything
        if user.is_admin():
            raise Return((True, set([])))

        # If user being updated can update everything apart from role
        if user.id == self.id:
            fields = ({'role'} & set(kwargs.keys()))
            if fields:
                raise Return((False, fields))
            else:
                raise Return((True, set([])))

        # Otherwise, cannot update
        raise Return((False, set([])))

    @coroutine
    def check_unique(self):
        """Check the user's email is unique"""
        emails = yield self.view.values(key=self.email)
        user_id = getattr(self, 'id', None)
        users = {x for x in emails if x != user_id and x}

        if users:
            raise exceptions.ValidationError(
                "User with email '{}' already exists".format(self.email))

    @classmethod
    @coroutine
    def create(cls, user, password, **kwargs):
        if not kwargs['pre_verified'] or user is None or not user.is_reseller(): 
            kwargs['verification_hash'] = unicode(uuid.uuid4().hex)

        resource = cls(password=cls.<PASSWORD>(password), **kwargs)
        yield resource._save()

        raise Return(resource)

    @classmethod
    @coroutine
    def create_admin(cls, email, password, **kwargs):
        """
        Create an approved 'global' administrator

        :param email: the user's email address
        :param password: <PASSWORD>
        :returns: a User
        """
        data = {
            'email': email,
            'password': <PASSWORD>(password),
            'has_agreed_to_terms': True,<commit_msg>fixed pre_verified check<commit_after># -*- coding: utf-8 -*-
# Copyright 2016 Open Permissions Platform Coalition
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed under the License is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.
from __future__ import unicode_literals

import uuid
from datetime import datetime, timedelta
from enum import Enum

import couch
from dateutil import parser
from passlib.hash import pbkdf2_sha256
from tornado.gen import coroutine, Return
from tornado.options import options, define
from voluptuous import All, Extra, In, Length, Required, Schema

from . import views, exceptions, validators
from .model import Document, SubResource, State


__all__ = ['User', 'UserOrganisation', 'Token']

define('min_length_password', default=3)
define('max_length_password', default=128)
define('token_ttl', default=86400)


class User(Document):
    resource_type = 'user'
    view = views.users
    active_view = views.active_users
    db_name = 'registry'
    internal_fields = (Document.internal_fields +
                       ['password', 'verification_hash', 'pre_verified'])
    read_only_fields = ['created_by']

    default_state = State.approved
    editable_states = [State.approved]

    # State transitions for users overridden so that:
    # - Only valid states are approved and deactivated
    # - Once user is deactivated cannot be reactivated
    approval_state_transitions = {}

    state_transitions = {
        None: [State.approved.name],
        State.approved.name: [State.deactivated.name]
    }

    class roles(Enum):
        administrator = 'administrator'
        user = 'user'
        reseller = 'reseller'
        default = user

    @property
    def schema(cls):
        orgs_schema = Schema({
            Extra: {
                'state': validators.validate_state,
                'role': In([x.value for x in cls.roles])
            }
        }, required=True)

        password_length = Length(min=options.min_length_password,
                                 max=options.max_length_password)

        schema = Schema({
            '_id': unicode,
            '_rev': unicode,
            'doc_version': unicode,
            Required('type', default=cls.resource_type): cls.resource_type,
            Required('email'): validators.valid_email,
            Required('password'): All(unicode, password_length),
            Required('has_agreed_to_terms'): True,
            Required('organisations', default={}): orgs_schema,
            Required('state', default=User.default_state.name): validators.validate_user_state,
            Required('role', default=User.roles.default.value): In([x.value for x in User.roles]),
            'first_name': unicode,
            'last_name': unicode,
            'phone': unicode,
            'verification_hash': unicode,
            'pre_verified': bool, 
            'actor': unicode  # only used in fixtures, should it be removed?
        })

        return schema

    def clean(self):
        """Verified value is derived from whether user has a verification hash"""
        result = super(User, self).clean()
        result['verified'] = 'verification_hash' not in self._resource
        return result

    @coroutine
    def can_update(self, user, **kwargs):
        # Sys admin can update everything
        if user.is_admin():
            raise Return((True, set([])))

        # If user being updated can update everything apart from role
        if user.id == self.id:
            fields = ({'role'} & set(kwargs.keys()))
            if fields:
                raise Return((False, fields))
            else:
                raise Return((True, set([])))

        # Otherwise, cannot update
        raise Return((False, set([])))

    @coroutine
    def check_unique(self):
        """Check the user's email is unique"""
        emails = yield self.view.values(key=self.email)
        user_id = getattr(self, 'id', None)
        users = {x for x in emails if x != user_id and x}

        if users:
            raise exceptions.ValidationError(
                "User with email '{}' already exists".format(self.email))

    @classmethod
    @coroutine
    def create(cls, user, password, **kwargs):
        if not kwargs.get('pre_verified', False) or user is None or not user.is_reseller(): 
            kwargs['verification_hash'] = unicode(uuid.uuid4().hex)

        resource = cls(password=<PASSWORD>.<PASSWORD>(password), **kwargs)
        yield resource._save()

        raise Return(resource)

    @classmethod
    @coroutine
    def create_admin(cls, email, password, **kwargs):
        """
        Create an approved 'global' administrator

        :param email: the user's email address
        :param password: <PASSWORD> user's <PASSWORD>
        :returns: a User
        """
        data = {
            'email': email,
            'password': <PASSWORD>.<PASSWORD>(password),
            'has_agreed_to_terms': True,
{'fraction_non_alphanumeric': 0.10287756253526424, 'fraction_numerical': 0.0043257476020312205, 'mean_word_length': 2.5904794058068874, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '7505805', 'n_tokens_mistral': 2904, 'n_tokens_neox': 2780, 'n_words': 894}