<commit_before>""":class:`FLARE_Calculator` is a calculator compatible with `ASE`. You can build up `ASE Atoms` for your atomic structure, and use `get_forces`, `get_potential_energy` as general `ASE Calculators`, and use it in `ASE Molecular Dynamics` and our `ASE OTF` training module."""
import numpy as np
import multiprocessing as mp
from flare.env import AtomicEnvironment
from flare.struc import Structure
from flare.mgp.mgp import MappedGaussianProcess
from flare.predict import predict_on_structure_par_en, predict_on_structure_en
from ase.calculators.calculator import Calculator

class FLARE_Calculator(Calculator):
    """Build FLARE as an ASE Calculator, which is compatible with ASE Atoms and Molecular Dynamics.

    :param gp_model: FLARE's Gaussian process object
    :type gp_model: GaussianProcess
    :param mgp_model: FLARE's Mapped Gaussian Process object. `None` by default. MGP will only be used if `use_mapping` is set to True
    :type mgp_model: MappedGaussianProcess
    :param par: set to `True` if parallelize the prediction. `False` by default.
    :type par: Bool
    :param use_mapping: set to `True` if use MGP for prediction. `False` by default.
    :type use_mapping: Bool
    """

    def __init__(self, gp_model, mgp_model=None, par=False, use_mapping=False):
        super().__init__() # all set to default values, TODO: change
        self.mgp_model = mgp_model
        self.gp_model = gp_model
        self.use_mapping = use_mapping
        self.par = par
        self.results = {}

    def get_property(self, atoms, property_name):
        if property_name not in self.results.keys():
            self.calculate(atoms)
        return self.results[property_name]

    def get_potential_energy(self, atoms=None, force_consistent=False):
        if self.use_mapping:
            print('MGP energy mapping not implemented, temporarily set to 0')
        return self.get_property(atoms, 'energy')

    def get_forces(self, atoms):
        return self.get_property(atoms, 'forces')

    def get_stress(self, atoms):
        if not self.use_mapping:
            raise NotImplementedError("Stress is only supported in MGP")
        return self.get_property(atoms, 'stress')

    def get_uncertainties(self, atoms):
        return self.get_property(atoms, 'stds')

    def calculate(self, atoms):
        '''
        calculate properties including: energy, local energies, forces, stress, uncertainties

        :param atoms: ASE Atoms object
        :type atoms: Atoms
        '''
        if self.use_mapping:
            if self.par:
                self.calculate_mgp_par(atoms)
            else:
                self.calculate_mgp_serial(atoms)
        else:
            self.calculate_gp(atoms)

    def calculate_gp(self, atoms):
        nat = len(atoms)
        struc_curr = Structure(np.array(atoms.cell),
                               atoms.get_atomic_numbers(),
                               atoms.positions)

        if self.par:
            forces, stds, local_energies = \
                    predict_on_structure_par_en(struc_curr, self.gp_model)
        else:
            forces, stds, local_energies = \
                    predict_on_structure_en(struc_curr, self.gp_model)

        self.results['forces'] = forces
        self.results['stds'] = stds
        self.results['local_energies'] = local_energies
        self.results['energy'] = np.sum(local_energies)
        atoms.get_uncertainties = self.get_uncertainties
        return forces

    def calculate_mgp_serial(self, atoms):
        nat = len(atoms)
        struc_curr = Structure(np.array(atoms.cell),
                               atoms.get_atomic_numbers(),
                               atoms.positions)

        forces = np.zeros((nat, 3))
        stress = np.zeros((nat, 6))
        stds = np.zeros((nat, 3))
        for n in range(nat):
            chemenv = AtomicEnvironment(struc_curr, n,
                                        self.mgp_model.cutoffs)
            f, v, vir = self.mgp_model.predict(chemenv, mean_only=False)
            forces[n] = f
            stress[n] = vir
            stds[n] = np.sqrt(np.absolute(v))

        self.results['forces'] = forces
        self.results['stds'] = stds
        self.results['stresses'] = stress
        self.results['stress'] = np.sum(stress, axis=0)

        # TODO: implement energy mapping
        self.results['local_energies'] = np.zeros(forces.shape)
        self.results['energy'] = 0

        atoms.get_uncertainties = self.get_uncertainties
        return forces

    def calculate_mgp_par(self, atoms):
        return self.calculate_mgp_serial(atoms)

    def calculation_required(self, atoms, quantities):
        return True

    def train_gp(self, **kwargs):
        """
        The same function of training GP hyperparameters as `train()` in :class:`GaussianProcess`
        """
        self.gp_model.train(**kwargs)

    def build_mgp(self, skip=True):
        """
        Construct :class:`MappedGaussianProcess` based on the current GP
        :param skip: if `True`, then it will not construct MGP
        :type skip: Bool
        """
        # l_bound not implemented

        if skip:
            return 1

        # set svd rank based on the training set, grid number and threshold 1000
        grid_params = self.mgp_model.grid_params
        struc_params = self.mgp_model.struc_params
        lmp_file_name = self.mgp_model.lmp_file_name
        mean_only = self.mgp_model.mean_only
        container_only = False

        train_size = len(self.gp_model.training_data)
        rank_2 = np.min([1000, grid_params['grid_num_2'], train_size*3])
        rank_3 = np.min([1000, grid_params['grid_num_3'][0]**3, train_size*3])
        grid_params['svd_rank_2'] = rank_2
        grid_params['svd_rank_3'] = rank_3

        hyps = self.gp_model.hyps
        cutoffs = self.gp_model.cutoffs
        self.mgp_model = MappedGaussianProcess(hyps, cutoffs,
                        grid_params, struc_params, mean_only,
                        container_only, self.gp_model, lmp_file_name)

<commit_msg>close #133<commit_after>""":class:`FLARE_Calculator` is a calculator compatible with `ASE`. You can build up `ASE Atoms` for your atomic structure, and use `get_forces`, `get_potential_energy` as general `ASE Calculators`, and use it in `ASE Molecular Dynamics` and our `ASE OTF` training module."""
import numpy as np
import multiprocessing as mp
from flare.env import AtomicEnvironment
from flare.struc import Structure
from flare.mgp.mgp import MappedGaussianProcess
from flare.predict import predict_on_structure_par_en, predict_on_structure_en
from ase.calculators.calculator import Calculator

class FLARE_Calculator(Calculator):
    """Build FLARE as an ASE Calculator, which is compatible with ASE Atoms and Molecular Dynamics.

    :param gp_model: FLARE's Gaussian process object
    :type gp_model: GaussianProcess
    :param mgp_model: FLARE's Mapped Gaussian Process object. `None` by default. MGP will only be used if `use_mapping` is set to True
    :type mgp_model: MappedGaussianProcess
    :param par: set to `True` if parallelize the prediction. `False` by default.
    :type par: Bool
    :param use_mapping: set to `True` if use MGP for prediction. `False` by default.
    :type use_mapping: Bool
    """

    def __init__(self, gp_model, mgp_model=None, par=False, use_mapping=False):
        super().__init__() # all set to default values, TODO: change
        self.mgp_model = mgp_model
        self.gp_model = gp_model
        self.use_mapping = use_mapping
        self.par = par
        self.results = {}

    def get_property(self, name, atoms=None, allow_calculation=True):
        if name not in self.results.keys():
            self.calculate(atoms)
        return self.results[name]

    def get_potential_energy(self, atoms=None, force_consistent=False):
        if self.use_mapping:
            print('MGP energy mapping not implemented, temporarily set to 0')
        return self.get_property('energy', atoms)

    def get_forces(self, atoms):
        return self.get_property('forces', atoms)

    def get_stress(self, atoms):
        if not self.use_mapping:
            raise NotImplementedError("Stress is only supported in MGP")
        return self.get_property('stress', atoms)

    def get_uncertainties(self, atoms):
        return self.get_property('stds', atoms)

    def calculate(self, atoms):
        '''
        calculate properties including: energy, local energies, forces, stress, uncertainties

        :param atoms: ASE Atoms object
        :type atoms: Atoms
        '''
        if self.use_mapping:
            if self.par:
                self.calculate_mgp_par(atoms)
            else:
                self.calculate_mgp_serial(atoms)
        else:
            self.calculate_gp(atoms)

    def calculate_gp(self, atoms):
        nat = len(atoms)
        struc_curr = Structure(np.array(atoms.cell),
                               atoms.get_atomic_numbers(),
                               atoms.positions)

        if self.par:
            forces, stds, local_energies = \
                    predict_on_structure_par_en(struc_curr, self.gp_model)
        else:
            forces, stds, local_energies = \
                    predict_on_structure_en(struc_curr, self.gp_model)

        self.results['forces'] = forces
        self.results['stds'] = stds
        self.results['local_energies'] = local_energies
        self.results['energy'] = np.sum(local_energies)
        atoms.get_uncertainties = self.get_uncertainties
        return forces

    def calculate_mgp_serial(self, atoms):
        nat = len(atoms)
        struc_curr = Structure(np.array(atoms.cell),
                               atoms.get_atomic_numbers(),
                               atoms.positions)

        forces = np.zeros((nat, 3))
        stress = np.zeros((nat, 6))
        stds = np.zeros((nat, 3))
        for n in range(nat):
            chemenv = AtomicEnvironment(struc_curr, n,
                                        self.mgp_model.cutoffs)
            f, v, vir = self.mgp_model.predict(chemenv, mean_only=False)
            forces[n] = f
            stress[n] = vir
            stds[n] = np.sqrt(np.absolute(v))

        self.results['forces'] = forces
        self.results['stds'] = stds
        self.results['stresses'] = stress
        self.results['stress'] = np.sum(stress, axis=0)

        # TODO: implement energy mapping
        self.results['local_energies'] = np.zeros(forces.shape)
        self.results['energy'] = 0

        atoms.get_uncertainties = self.get_uncertainties
        return forces

    def calculate_mgp_par(self, atoms):
        return self.calculate_mgp_serial(atoms)

    def calculation_required(self, atoms, quantities):
        return True

    def train_gp(self, **kwargs):
        """
        The same function of training GP hyperparameters as `train()` in :class:`GaussianProcess`
        """
        self.gp_model.train(**kwargs)

    def build_mgp(self, skip=True):
        """
        Construct :class:`MappedGaussianProcess` based on the current GP
        :param skip: if `True`, then it will not construct MGP
        :type skip: Bool
        """
        # l_bound not implemented

        if skip:
            return 1

        # set svd rank based on the training set, grid number and threshold 1000
        grid_params = self.mgp_model.grid_params
        struc_params = self.mgp_model.struc_params
        lmp_file_name = self.mgp_model.lmp_file_name
        mean_only = self.mgp_model.mean_only
        container_only = False

        train_size = len(self.gp_model.training_data)
        rank_2 = np.min([1000, grid_params['grid_num_2'], train_size*3])
        rank_3 = np.min([1000, grid_params['grid_num_3'][0]**3, train_size*3])
        grid_params['svd_rank_2'] = rank_2
        grid_params['svd_rank_3'] = rank_3

        hyps = self.gp_model.hyps
        cutoffs = self.gp_model.cutoffs
        self.mgp_model = MappedGaussianProcess(hyps, cutoffs,
                        grid_params, struc_params, mean_only,
                        container_only, self.gp_model, lmp_file_name)

<|endoftext|>


<commit_before># Copyright 2015 Vinicius Chiele. All rights reserved.
#
# 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 datetime import datetime
from .binders import BooleanBinder
from .binders import DateTimeBinder
from .binders import DictionaryBinder
from .binders import PrimitiveBinder
from .errors import InvalidArgumentError
from .errors import RequiredArgumentError

class Binder(object):
    binders = {
        bool: BooleanBinder(),
        dict: DictionaryBinder(),
        int: PrimitiveBinder(int),
        float: PrimitiveBinder(float),
        str: PrimitiveBinder(str),
        datetime: DateTimeBinder()
    }

    @staticmethod
    def bind(params):
        kwargs = {}

        for name, param in params.items():
            context = BindingContext()
            context.name = param.name or name
            context.type = param.type
            context.multiple = param.multiple

            try:
                param.prepare_context(context)

                binder = Binder.binders[param.type]

                value = binder.bind(context)
            except Exception as e:
                raise InvalidArgumentError(name, e)

            if value is None:
                if param.required:
                    raise RequiredArgumentError(name)
                value = param.default

            kwargs[name] = value

        return kwargs

class BindingContext(object):
    def __init__(self, name=None, values=None, multiple=False):
        self.name = name
        self.values = values
        self.multiple = multiple
<commit_msg>Removed empty lines<commit_after># Copyright 2015 Vinicius Chiele. All rights reserved.
#
# 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 datetime import datetime
from .binders import BooleanBinder
from .binders import DateTimeBinder
from .binders import DictionaryBinder
from .binders import PrimitiveBinder
from .errors import InvalidArgumentError
from .errors import RequiredArgumentError

class Binder(object):
    binders = {
        bool: BooleanBinder(),
        dict: DictionaryBinder(),
        int: PrimitiveBinder(int),
        float: PrimitiveBinder(float),
        str: PrimitiveBinder(str),
        datetime: DateTimeBinder()
    }

    @staticmethod
    def bind(params):
        kwargs = {}

        for name, param in params.items():
            context = BindingContext()
            context.name = param.name or name
            context.type = param.type
            context.multiple = param.multiple

            try:
                param.prepare_context(context)
                binder = Binder.binders[param.type]
                value = binder.bind(context)
            except Exception as e:
                raise InvalidArgumentError(name, e)

            if value is None:
                if param.required:
                    raise RequiredArgumentError(name)
                value = param.default

            kwargs[name] = value

        return kwargs

class BindingContext(object):
    def __init__(self, name=None, values=None, multiple=False):
        self.name = name
        self.values = values
        self.multiple = multiple
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
flask_cacheobj.cache
~~~~~~~~~~~~~~~~~~~~

Cache functions.

:copyright: (c) 2016 Liwushuo Inc.
:license: MIT, see LICENSE for more details.
"""

import inspect
import logging
from functools import wraps

import msgpack
from decorator import decorator

from .msgpackable import encode, decode
from .format import format_key_pattern
from .proxy import mc
from ._compat import text_type, integer_types

logger = logging.getLogger('flask.cache')

def gen_key_factory(key_pattern, arg_names, defaults):
    args = dict(zip(arg_names[-len(defaults):], defaults)) if defaults else {}
    if callable(key_pattern):
        names = inspect.getargspec(key_pattern)[0]
    def gen_key(*a, **kw):
        aa = args.copy()
        aa.update(zip(arg_names, a))
        aa.update(kw)
        if callable(key_pattern):
            key = key_pattern(*[aa[n] for n in names])
        else:
            key = format_key_pattern(key_pattern, *[aa[n] for n in arg_names], **aa)
        return key and key.replace(' ','_'), aa
    return gen_key

def cache_obj(cache_key_reg, packer=encode, unpacker=decode):
    """A decorator that can cache function result.

    Example::

        @cache_obj({'key': 'item:{item_id}'})
        def get_item(item_id):
            return Item.query.get(item_id)

        @cache_obj({'key': 'item:{item_id}', 'expire': 60})
        def get_item(item_id):
            return Item.query.get(item_id)

        @cache_obj({'key': lambda filename: md5(filename).hexdigest()})
        def get_file(filename):
            return remote.fetch_file_data(filename)

    :param cache_key_reg: a dict-like object contains `key` and `expire`. `cache_obj` will inspect function arguments and format `cache_key_reg['key']` if `cache_key_reg['key']` is a string, or apply them to `cache_key_reg['key']` if `cache_key_reg['key']` is a callable object. `expire` is an integer. The unit is second.
    :param packer: a method used to encode function result.
    :param unpacker: a callable object used to decode msgpack data.
    """
    @decorator
    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        r = mc.get(key)

        r = unpacker(r) if r else None

        # set cache
        if r is None:
            r = f(*a, **kw)
            if r is not None:
                mc.set(key, packer(r), expire)
        else:
            logger.info('Cache read - %s',key)

        return r

    deco.cache_key_reg = cache_key_reg
    deco.packer = packer
    deco.unpacker = unpacker

    return deco

def delete_obj(cache_key_reg):
    """Delete object cache

    :param cache_key_reg: a dict-like object contains 'key'.
    """
    @decorator
    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, _ = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        ret = f(*a, **kw)
        mc.delete(key)
        return ret

    deco.cache_key_reg = cache_key_reg
    deco.packer = None
    deco.unpacker = None

    return deco

def delete_cache(cache_key_reg, **kw):
    """Delete cache via cache key registry.

    Example::

        delete_cache({'key': 'item:{item_id}'}, item_id=1)
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    mc.delete(key)
    logger.info('Cache delete - %s', key)

def cache_hash(cache_key_reg, packer=encode, unpacker=decode):
    """A decorator that can cache function result into redis hash.

    Example::

        @cache_hash({'key': '{item_id}', 'hash_key': 'item'})
        def get_item(item_id):
            return Item.query.get(item_id)

    :param cache_key_reg: a dict-like object contains `hash_key`, `key`. `hash_key` is a string as redis hash key. `key` is a template string or a callable object as hash field key.
    """
    @decorator
    def deco(f, *a, **kw):
        hash_key = cache_key_reg.get('hash_key')
        key = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception('do not support varargs')
        gen_key = gen_key_factory(key, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode('utf8')
        r = mc.hget(hash_key, key)
        if r is not None:
            logger.info('HashCache read - %s - %s', hash_key, key)
            return unpacker(r) if r else None

        r = f(*a, **kw)
        if r is None:
            return r
        mc.hset(hash_key, key, packer(r))
        return r

    deco.cache_key_reg = cache_key_reg
    deco.packer = packer
    deco.unpacker = unpacker

    return deco

def hash_del(cache_key_reg, **kw):
    """Delete hash field.

    Example::

        hash_del({'key': '{item_id}', 'hash_key': 'item'}, item_id=1)
    """
    hash_key = cache_key_reg.get('hash_key')
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)
    mc.hdel(hash_key, key)

def cache_list(cache_key_reg, packer=str, unpacker=int):
    @decorator
    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        r = mc.smembers(key)
        r = [unpacker(rv) for rv in r]

        # set cache
        if not r:
            r = f(*a, **kw)
            if r:
                mc.delete(key)
                mc.sadd(key, *map(packer, r))
        else:
            logger.info('Cache read - %s', key)

        return r

    deco.cache_key_reg = cache_key_reg
    deco.unpacker = unpacker
    deco.packer = packer

    return deco

def list_add(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    mc.sadd(key, value)

def list_rem(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    mc.srem(key, value)

def list_len(cache_key_reg, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.scard(key)

def exists(cache_key_reg, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.exists(key)

def set_add(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.sadd(key, value)

def set_rem(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.srem(key, value)

def set_len(cache_key_reg, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.scard(key)

def cache_counter(cache_key_reg, packer=str, unpacker=int):
    """A decorator that can cache counter function result.

    Example::

        @cache_counter({'key': 'group_members:{group_id}', 'expire': 60})
        def get_group_members_count(group_id):
            return GroupMember.query.filter_by(group_id).count()

    :param cache_key_reg: a dict-like object contains `key` and `expire`.
    """
    @decorator
    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        r = mc.get(key)

        if r is None:
            r = f(*a, **kw)
            if r is not None:
                if not isinstance(r, integer_types):
                    raise Exception("Only support cache counters!")
                mc.set(key, packer(r), expire)
        else:
            logger.info('Counter cache read - %s', key)

        return unpacker(r)

    deco.cache_key_reg = cache_key_reg
    deco.packer = packer
    deco.unpacker = unpacker

    return deco

def inc_counter(cache_key_reg, delta=1, **kw):
    """Increase counter value.

    :param cache_key_reg:  a dict-like object contains `key`.
    :param delta: increment delta. an integer. Default is 1.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    mc.incr(key, delta)

def dec_counter(cache_key_reg, delta=1, **kw):
    """Decrease counter value.

    :param cache_key_reg: a dict-like object contains `key`.
    :param delta: decrement delta. an integer. Default is 1.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    mc.decr(key, delta)

def get_counter(cache_key_reg, **kw):
    """Get counter value.

    :param cache_key_reg: a dict-like object contains `key`.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    return int(mc.get(key) or 0)

def set_counter(cache_key_reg, value=0, **kw):
    """Set counter value.

    :param cache_key_reg: a dict-like object contains `key`.
    :param value: reset counter value with given value.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    return mc.set(key, value)

def reset_counter(cache_key_reg, **kw):
    """Reset counter value, alias for set_counter(cache_key_reg, 0, **kw)"""
    return set_counter(cache_key_reg, 0, **kw)
<commit_msg>Attach packer to f.<commit_after># -*- coding: utf-8 -*-
"""
flask_cacheobj.cache
~~~~~~~~~~~~~~~~~~~~

Cache functions.

:copyright: (c) 2016 Liwushuo Inc.
:license: MIT, see LICENSE for more details.
"""

import inspect
import logging
from functools import wraps

import msgpack
from decorator import decorator, decorate

from .msgpackable import encode, decode
from .format import format_key_pattern
from .proxy import mc
from .utils import make_deco
from ._compat import text_type, integer_types

logger = logging.getLogger('flask.cache')

def gen_key_factory(key_pattern, arg_names, defaults):
    args = dict(zip(arg_names[-len(defaults):], defaults)) if defaults else {}
    if callable(key_pattern):
        names = inspect.getargspec(key_pattern)[0]
    def gen_key(*a, **kw):
        aa = args.copy()
        aa.update(zip(arg_names, a))
        aa.update(kw)
        if callable(key_pattern):
            key = key_pattern(*[aa[n] for n in names])
        else:
            key = format_key_pattern(key_pattern, *[aa[n] for n in arg_names], **aa)
        return key and key.replace(' ','_'), aa
    return gen_key

def cache_obj(cache_key_reg, packer=encode, unpacker=decode):
    """A decorator that can cache function result.

    Example::

        @cache_obj({'key': 'item:{item_id}'})
        def get_item(item_id):
            return Item.query.get(item_id)

        @cache_obj({'key': 'item:{item_id}', 'expire': 60})
        def get_item(item_id):
            return Item.query.get(item_id)

        @cache_obj({'key': lambda filename: md5(filename).hexdigest()})
        def get_file(filename):
            return remote.fetch_file_data(filename)

    :param cache_key_reg: a dict-like object contains `key` and `expire`. `cache_obj` will inspect function arguments and format `cache_key_reg['key']` if `cache_key_reg['key']` is a string, or apply them to `cache_key_reg['key']` if `cache_key_reg['key']` is a callable object. `expire` is an integer. The unit is second.
    :param packer: a method used to encode function result.
    :param unpacker: a callable object used to decode msgpack data.
    """

    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        r = mc.get(key)

        r = unpacker(r) if r else None

        # set cache
        if r is None:
            r = f(*a, **kw)
            if r is not None:
                mc.set(key, packer(r), expire)
        else:
            logger.info('Cache read - %s',key)

        return r

    return make_deco(
        deco,
        cache_key_reg=cache_key_reg,
        packer=packer,
        unpacker=unpacker,
    )

def delete_obj(cache_key_reg):
    """Delete object cache

    :param cache_key_reg: a dict-like object contains 'key'.
    """

    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, _ = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        ret = f(*a, **kw)
        mc.delete(key)
        return ret

    return make_deco(
        deco,
        cache_key_reg=cache_key_reg,
    )

def delete_cache(cache_key_reg, **kw):
    """Delete cache via cache key registry.

    Example::

        delete_cache({'key': 'item:{item_id}'}, item_id=1)
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    mc.delete(key)
    logger.info('Cache delete - %s', key)

def cache_hash(cache_key_reg, packer=encode, unpacker=decode):
    """A decorator that can cache function result into redis hash.

    Example::

        @cache_hash({'key': '{item_id}', 'hash_key': 'item'})
        def get_item(item_id):
            return Item.query.get(item_id)

    :param cache_key_reg: a dict-like object contains `hash_key`, `key`. `hash_key` is a string as redis hash key. `key` is a template string or a callable object as hash field key.
    """

    def deco(f, *a, **kw):
        hash_key = cache_key_reg.get('hash_key')
        key = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception('do not support varargs')
        gen_key = gen_key_factory(key, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode('utf8')
        r = mc.hget(hash_key, key)
        if r is not None:
            logger.info('HashCache read - %s - %s', hash_key, key)
            return unpacker(r) if r else None

        r = f(*a, **kw)
        if r is None:
            return r
        mc.hset(hash_key, key, packer(r))
        return r

    return make_deco(
        deco,
        cache_key_reg=cache_key_reg,
        packer=packer,
        unpacker=unpacker,
    )

def hash_del(cache_key_reg, **kw):
    """Delete hash field.

    Example::

        hash_del({'key': '{item_id}', 'hash_key': 'item'}, item_id=1)
    """
    hash_key = cache_key_reg.get('hash_key')
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)
    mc.hdel(hash_key, key)

def cache_list(cache_key_reg, packer=str, unpacker=int):

    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        r = mc.smembers(key)
        r = [unpacker(rv) for rv in r]

        # set cache
        if not r:
            r = f(*a, **kw)
            if r:
                mc.delete(key)
                mc.sadd(key, *map(packer, r))
        else:
            logger.info('Cache read - %s', key)

        return r

    return make_deco(
        deco,
        cache_key_reg=cache_key_reg,
        packer=packer,
        unpacker=unpacker,
    )

def list_add(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    mc.sadd(key, value)

def list_rem(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    mc.srem(key, value)

def list_len(cache_key_reg, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.scard(key)

def exists(cache_key_reg, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.exists(key)

def set_add(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.sadd(key, value)

def set_rem(cache_key_reg, value, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.srem(key, value)

def set_len(cache_key_reg, **kw):
    key_pattern = cache_key_reg.get('key')
    expire = cache_key_reg.get('expire', None)
    key = format_key_pattern(key_pattern, **kw)

    return mc.scard(key)

def cache_counter(cache_key_reg, packer=str, unpacker=int):
    """A decorator that can cache counter function result.

    Example::

        @cache_counter({'key': 'group_members:{group_id}', 'expire': 60})
        def get_group_members_count(group_id):
            return GroupMember.query.filter_by(group_id).count()

    :param cache_key_reg: a dict-like object contains `key` and `expire`.
    """

    def deco(f, *a, **kw):
        key_pattern = cache_key_reg.get('key')
        expire = cache_key_reg.get('expire', None)
        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
        if varargs or varkw:
            raise Exception("do not support varargs")
        gen_key = gen_key_factory(key_pattern, arg_names, defaults)

        key, args = gen_key(*a, **kw)
        if not key:
            return f(*a, **kw)
        if isinstance(key, text_type):
            key = key.encode("utf8")
        r = mc.get(key)

        if r is None:
            r = f(*a, **kw)
            if r is not None:
                if not isinstance(r, integer_types):
                    raise Exception("Only support cache counters!")
                mc.set(key, packer(r), expire)
        else:
            logger.info('Counter cache read - %s', key)

        return unpacker(r)

    return make_deco(
        deco,
        cache_key_reg=cache_key_reg,
        packer=packer,
        unpacker=unpacker,
    )

def inc_counter(cache_key_reg, delta=1, **kw):
    """Increase counter value.

    :param cache_key_reg:  a dict-like object contains `key`.
    :param delta: increment delta. an integer. Default is 1.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    mc.incr(key, delta)

def dec_counter(cache_key_reg, delta=1, **kw):
    """Decrease counter value.

    :param cache_key_reg: a dict-like object contains `key`.
    :param delta: decrement delta. an integer. Default is 1.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    mc.decr(key, delta)

def get_counter(cache_key_reg, **kw):
    """Get counter value.

    :param cache_key_reg: a dict-like object contains `key`.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    return int(mc.get(key) or 0)

def set_counter(cache_key_reg, value=0, **kw):
    """Set counter value.

    :param cache_key_reg: a dict-like object contains `key`.
    :param value: reset counter value with given value.
    :param kw: A dict of named parameters used to format `cache_key_reg['key']`.
    """
    key_pattern = cache_key_reg.get('key')
    key = format_key_pattern(key_pattern, **kw)

    return mc.set(key, value)

def reset_counter(cache_key_reg, **kw):
    """Reset counter value, alias for set_counter(cache_key_reg, 0, **kw)"""
    return set_counter(cache_key_reg, 0, **kw)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    flask.ext.security.views
    ~~~~~~~~~~~~~~~~~~~~~~~~

    Flask-Security views module

    :copyright: (c) 2012 by Matt Wright.
    :license: MIT, see LICENSE for more details.
"""

from flask import current_app as app, redirect, request, \
     render_template, jsonify, Blueprint
from werkzeug.datastructures import MultiDict
from werkzeug.local import LocalProxy

from flask_security.confirmable import confirm_by_token, reset_confirmation_token
from flask_security.core import current_user
from flask_security.decorators import login_required
from flask_security.exceptions import ConfirmationError, BadCredentialsError, \
     ResetPasswordError, PasswordlessLoginError
from flask_security.forms import LoginForm, RegisterForm, ForgotPasswordForm, \
     ResetPasswordForm, ResendConfirmationForm, PasswordlessLoginForm
from flask_security.passwordless import send_login_instructions, login_by_token
from flask_security.recoverable import reset_by_token, \
     reset_password_reset_token
from flask_security.signals import user_registered
from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
     get_message, config_value, login_user, logout_user, url_for_security

# Convenient references
_security = LocalProxy(lambda: app.extensions['security'])

_datastore = LocalProxy(lambda: _security.datastore)

_logger = LocalProxy(lambda: app.logger)

def _json_auth_ok(user):
    return jsonify({
        "meta": {
            "code": 200
        },
        "response": {
            "user": {
                "id": str(user.id),
                "authentication_token": user.get_auth_token()
            }
        }
    })

def _json_auth_error(msg):
    resp = jsonify({
        "meta": {
            "code": 400
        },
        "response": {
            "error": msg
        }
    })
    resp.status_code = 400
    return resp

def authenticate():
    """View function which handles an authentication request."""
    form_data = MultiDict(request.json) if request.json else request.form
    form = LoginForm(form_data)

    try:
        user = _security.auth_provider.authenticate(form)

        if login_user(user, remember=form.remember.data):
            if request.json:
                return _json_auth_ok(user)

            return redirect(get_post_login_redirect())

        raise BadCredentialsError(get_message('DISABLED_ACCOUNT')[0])

    except BadCredentialsError, e:
        msg = str(e)

    _logger.debug('Unsuccessful authentication attempt: %s' % msg)

    if request.json:
        return _json_auth_error(msg)

    do_flash(msg, 'error')

    return redirect(request.referrer or url_for_security('login'))

def login():
    form = PasswordlessLoginForm() if _security.passwordless else LoginForm()
    template = 'send_login' if _security.passwordless else 'login'
    return render_template('security/%s.html' % template, login_form=form)

def logout():
    """View function which handles a logout request."""

    logout_user()

    _logger.debug('User logged out')

    return redirect(request.args.get('next', None) or
                    get_url(_security.post_logout_view))

def register_user():
    """View function which handles a registration request."""

    form = RegisterForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        # Create user
        u = _datastore.create_user(**form.to_dict())

        # Send confirmation instructions if necessary
        t = reset_confirmation_token(u) if _security.confirmable else None

        data = dict(user=u, confirm_token=t)
        user_registered.send(data, app=app._get_current_object())

        _logger.debug('User %s registered' % u)

        # Login the user if allowed
        if not _security.confirmable or _security.login_without_confirmation:
            login_user(u)

        return redirect(get_url(_security.post_register_view) or
                        get_url(_security.post_login_view))

    return render_template('security/register_user.html',
                           register_user_form=form)

def send_login():
    form = PasswordlessLoginForm()

    user = _datastore.find_user(**form.to_dict())

    if user.is_active():
        send_login_instructions(user, form.next.data)
        do_flash(*get_message('LOGIN_EMAIL_SENT', email=user.email))
    else:
        do_flash(*get_message('DISABLED_ACCOUNT'))

    return render_template('security/send_login.html', login_form=form)

def token_login(token):
    if current_user.is_authenticated():
        return redirect(get_url(_security.post_login_view))

    try:
        user, next = login_by_token(token)

    except PasswordlessLoginError, e:
        msg, cat = str(e), 'error'

        if e.user:
            send_login_instructions(e.user, e.next)

        do_flash(msg, cat)

        return redirect(request.referrer or url_for_security('login'))

    do_flash(*get_message('PASSWORDLESS_LOGIN_SUCCESSFUL'))

    return redirect(next or get_url(_security.post_login_view))

def send_confirmation():
    form = ResendConfirmationForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        user = _datastore.find_user(**form.to_dict())

        reset_confirmation_token(user)

        _logger.debug('%s request confirmation instructions' % user)

        msg, cat = get_message('CONFIRMATION_REQUEST', email=user.email)

        do_flash(msg, cat)

    return render_template('security/send_confirmation.html',
                           reset_confirmation_form=form)

def confirm_email(token):
    """View function which handles a email confirmation request."""
    try:
        user = confirm_by_token(token)
        _logger.debug('%s confirmed their email' % user)

    except ConfirmationError, e:
        msg, cat = str(e), 'error'

        _logger.debug('Confirmation error: ' + msg)

        if e.user:
            reset_confirmation_token(e.user)

        do_flash(msg, cat)

        return redirect(get_url(_security.confirm_error_view) or
                        url_for_security('send_confirmation'))

    do_flash(*get_message('EMAIL_CONFIRMED'))

    return redirect(get_url(_security.post_confirm_view) or
                    get_url(_security.post_login_view))

def forgot_password():
    """View function that handles a forgotten password request."""

    form = ForgotPasswordForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        user = _datastore.find_user(**form.to_dict())

        reset_password_reset_token(user)

        _logger.debug('%s requested to reset their password' % user)

        msg, cat = get_message('PASSWORD_RESET_REQUEST', email=user.email)

        do_flash(msg, cat)

        return redirect(get_url(_security.post_forgot_view))

    else:
        for key, value in form.errors.items():
            do_flash(value[0], 'error')

    return render_template('security/forgot_password.html',
                           forgot_password_form=form)

def reset_password(token):
    """View function that handles a reset password request."""

    form = ResetPasswordForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        try:
            user = reset_by_token(token=token, **form.to_dict())

            _logger.debug('%s reset their password' % user)

            do_flash(*get_message('PASSWORD_RESET'))

            login_user(user)

            return redirect(get_url(_security.post_reset_view) or
                            get_url(_security.post_login_view))

        except ResetPasswordError, e:
            msg, cat = str(e), 'error'

            _logger.debug('Password reset error: ' + msg)

            if e.user:
                reset_password_reset_token(e.user)

                msg, cat = get_message('PASSWORD_RESET_EXPIRED',
                                       within=_security.reset_password_within,
                                       email=e.user.email)

            do_flash(msg, cat)

    return render_template('security/reset_password.html',
                           reset_password_form=form,
                           password_reset_token=token)

def create_blueprint(app, name, import_name, **kwargs):
    bp = Blueprint(name, import_name, **kwargs)

    if config_value('PASSWORDLESS', app=app):
        bp.route(config_value('AUTH_URL', app=app),
                 methods=['POST'],
                 endpoint='send_login')(send_login)

        bp.route(config_value('AUTH_URL', app=app) + '/<token>',
                 methods=['GET'],
                 endpoint='token_login')(token_login)
    else:
        bp.route(config_value('AUTH_URL', app=app),
                 methods=['POST'],
                 endpoint='authenticate')(authenticate)

    bp.route(config_value('LOGIN_URL', app=app),
             endpoint='login')(login)

    bp.route(config_value('LOGOUT_URL', app=app),
             endpoint='logout')(login_required(logout))

    if config_value('REGISTERABLE', app=app):
        bp.route(config_value('REGISTER_URL', app=app),
                 methods=['GET', 'POST'],
                 endpoint='register')(register_user)

    if config_value('RECOVERABLE', app=app):
        bp.route(config_value('RESET_URL', app=app),
                 methods=['GET', 'POST'],
                 endpoint='forgot_password')(forgot_password)
        bp.route(config_value('RESET_URL', app=app) + '/<token>',
                 methods=['GET', 'POST'],
                 endpoint='reset_password')(reset_password)

    if config_value('CONFIRMABLE', app=app):
        bp.route(config_value('CONFIRM_URL', app=app),
                 methods=['GET', 'POST'],
                 endpoint='send_confirmation')(send_confirmation)
        bp.route(config_value('CONFIRM_URL', app=app) + '/<token>',
                 methods=['GET', 'POST'],
                 endpoint='confirm_email')(confirm_email)

    return bp
<commit_msg>Change function name<commit_after># -*- coding: utf-8 -*-
"""
    flask.ext.security.views
    ~~~~~~~~~~~~~~~~~~~~~~~~

    Flask-Security views module

    :copyright: (c) 2012 by Matt Wright.
    :license: MIT, see LICENSE for more details.
"""

from flask import current_app as app, redirect, request, \
     render_template, jsonify, Blueprint
from werkzeug.datastructures import MultiDict
from werkzeug.local import LocalProxy

from flask_security.confirmable import confirm_by_token, reset_confirmation_token
from flask_security.core import current_user
from flask_security.decorators import login_required
from flask_security.exceptions import ConfirmationError, BadCredentialsError, \
     ResetPasswordError, PasswordlessLoginError
from flask_security.forms import LoginForm, RegisterForm, ForgotPasswordForm, \
     ResetPasswordForm, ResendConfirmationForm, PasswordlessLoginForm
from flask_security.passwordless import send_login_instructions, login_by_token
from flask_security.recoverable import reset_by_token, \
     reset_password_reset_token
from flask_security.signals import user_registered
from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
     get_message, config_value, login_user, logout_user, url_for_security

# Convenient references
_security = LocalProxy(lambda: app.extensions['security'])

_datastore = LocalProxy(lambda: _security.datastore)

_logger = LocalProxy(lambda: app.logger)

def _json_auth_ok(user):
    return jsonify({
        "meta": {
            "code": 200
        },
        "response": {
            "user": {
                "id": str(user.id),
                "authentication_token": user.get_auth_token()
            }
        }
    })

def _json_auth_error(msg):
    resp = jsonify({
        "meta": {
            "code": 400
        },
        "response": {
            "error": msg
        }
    })
    resp.status_code = 400
    return resp

def authenticate():
    """View function which handles an authentication request."""
    form_data = MultiDict(request.json) if request.json else request.form
    form = LoginForm(form_data)

    try:
        user = _security.auth_provider.authenticate(form)

        if login_user(user, remember=form.remember.data):
            if request.json:
                return _json_auth_ok(user)

            return redirect(get_post_login_redirect())

        raise BadCredentialsError(get_message('DISABLED_ACCOUNT')[0])

    except BadCredentialsError, e:
        msg = str(e)

    _logger.debug('Unsuccessful authentication attempt: %s' % msg)

    if request.json:
        return _json_auth_error(msg)

    do_flash(msg, 'error')

    return redirect(request.referrer or url_for_security('login'))

def login():
    form = PasswordlessLoginForm() if _security.passwordless else LoginForm()
    template = 'send_login' if _security.passwordless else 'login'
    return render_template('security/%s.html' % template, login_form=form)

def logout():
    """View function which handles a logout request."""

    logout_user()

    _logger.debug('User logged out')

    return redirect(request.args.get('next', None) or
                    get_url(_security.post_logout_view))

def register():
    """View function which handles a registration request."""

    form = RegisterForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        # Create user
        u = _datastore.create_user(**form.to_dict())

        # Send confirmation instructions if necessary
        t = reset_confirmation_token(u) if _security.confirmable else None

        data = dict(user=u, confirm_token=t)
        user_registered.send(data, app=app._get_current_object())

        _logger.debug('User %s registered' % u)

        # Login the user if allowed
        if not _security.confirmable or _security.login_without_confirmation:
            login_user(u)

        return redirect(get_url(_security.post_register_view) or
                        get_url(_security.post_login_view))

    return render_template('security/register.html',
                           register_user_form=form)

def send_login():
    form = PasswordlessLoginForm()

    user = _datastore.find_user(**form.to_dict())

    if user.is_active():
        send_login_instructions(user, form.next.data)
        do_flash(*get_message('LOGIN_EMAIL_SENT', email=user.email))
    else:
        do_flash(*get_message('DISABLED_ACCOUNT'))

    return render_template('security/send_login.html', login_form=form)

def token_login(token):
    if current_user.is_authenticated():
        return redirect(get_url(_security.post_login_view))

    try:
        user, next = login_by_token(token)

    except PasswordlessLoginError, e:
        msg, cat = str(e), 'error'

        if e.user:
            send_login_instructions(e.user, e.next)

        do_flash(msg, cat)

        return redirect(request.referrer or url_for_security('login'))

    do_flash(*get_message('PASSWORDLESS_LOGIN_SUCCESSFUL'))

    return redirect(next or get_url(_security.post_login_view))

def send_confirmation():
    form = ResendConfirmationForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        user = _datastore.find_user(**form.to_dict())

        reset_confirmation_token(user)

        _logger.debug('%s request confirmation instructions' % user)

        msg, cat = get_message('CONFIRMATION_REQUEST', email=user.email)

        do_flash(msg, cat)

    return render_template('security/send_confirmation.html',
                           reset_confirmation_form=form)

def confirm_email(token):
    """View function which handles a email confirmation request."""
    try:
        user = confirm_by_token(token)
        _logger.debug('%s confirmed their email' % user)

    except ConfirmationError, e:
        msg, cat = str(e), 'error'

        _logger.debug('Confirmation error: ' + msg)

        if e.user:
            reset_confirmation_token(e.user)

        do_flash(msg, cat)

        return redirect(get_url(_security.confirm_error_view) or
                        url_for_security('send_confirmation'))

    do_flash(*get_message('EMAIL_CONFIRMED'))

    return redirect(get_url(_security.post_confirm_view) or
                    get_url(_security.post_login_view))

def forgot_password():
    """View function that handles a forgotten password request."""

    form = ForgotPasswordForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        user = _datastore.find_user(**form.to_dict())

        reset_password_reset_token(user)

        _logger.debug('%s requested to reset their password' % user)

        msg, cat = get_message('PASSWORD_RESET_REQUEST', email=user.email)

        do_flash(msg, cat)

        return redirect(get_url(_security.post_forgot_view))

    else:
        for key, value in form.errors.items():
            do_flash(value[0], 'error')

    return render_template('security/forgot_password.html',
                           forgot_password_form=form)

def reset_password(token):
    """View function that handles a reset password request."""

    form = ResetPasswordForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        try:
            user = reset_by_token(token=token, **form.to_dict())

            _logger.debug('%s reset their password' % user)

            do_flash(*get_message('PASSWORD_RESET'))

            login_user(user)

            return redirect(get_url(_security.post_reset_view) or
                            get_url(_security.post_login_view))

        except ResetPasswordError, e:
            msg, cat = str(e), 'error'

            _logger.debug('Password reset error: ' + msg)

            if e.user:
                reset_password_reset_token(e.user)

                msg, cat = get_message('PASSWORD_RESET_EXPIRED',
                                       within=_security.reset_password_within,
                                       email=e.user.email)

            do_flash(msg, cat)

    return render_template('security/reset_password.html',
                           reset_password_form=form,
                           password_reset_token=token)

def create_blueprint(app, name, import_name, **kwargs):
    bp = Blueprint(name, import_name, **kwargs)

    if config_value('PASSWORDLESS', app=app):
        bp.route(config_value('AUTH_URL', app=app),
                 methods=['POST'],
                 endpoint='send_login')(send_login)

        bp.route(config_value('AUTH_URL', app=app) + '/<token>',
                 methods=['GET'],
                 endpoint='token_login')(token_login)
    else:
        bp.route(config_value('AUTH_URL', app=app),
                 methods=['POST'],
                 endpoint='authenticate')(authenticate)

    bp.route(config_value('LOGIN_URL', app=app),
             endpoint='login')(login)

    bp.route(config_value('LOGOUT_URL', app=app),
             endpoint='logout')(login_required(logout))

    if config_value('REGISTERABLE', app=app):
        bp.route(config_value('REGISTER_URL', app=app),
                 methods=['GET', 'POST'],
                 endpoint='register')(register)

    if config_value('RECOVERABLE', app=app):
        bp.route(config_value('RESET_URL', app=app),
                 methods=['GET', 'POST'],
                 endpoint='forgot_password')(forgot_password)
        bp.route(config_value('RESET_URL', app=app) + '/<token>',
                 methods=['GET', 'POST'],
                 endpoint='reset_password')(reset_password)

    if config_value('CONFIRMABLE', app=app):
        bp.route(config_value('CONFIRM_URL', app=app),
                 methods=['GET', 'POST'],
                 endpoint='send_confirmation')(send_confirmation)
        bp.route(config_value('CONFIRM_URL', app=app) + '/<token>',
                 methods=['GET', 'POST'],
                 endpoint='confirm_email')(confirm_email)

    return bp
<|endoftext|>


<commit_before># -*- coding: utf8 -*-
"""Main package for Flask-Z3950."""

from .z3950 import Z3950Manager, Z3950Database
from .dataset import Dataset
from PyZ3950.zoom import ZoomError

__author__ = "Alexander Mendes"
__license__ = "BSD License"
__version__ = "0.2.3"
<commit_msg>Version bump<commit_after># -*- coding: utf8 -*-
"""Main package for Flask-Z3950."""

from .z3950 import Z3950Manager, Z3950Database
from .dataset import Dataset
from PyZ3950.zoom import ZoomError

__author__ = "Alexander Mendes"
__license__ = "BSD License"
__version__ = "0.2.4"
<|endoftext|>


<commit_before>from wtforms import (TextField, IntegerField, FloatField, BooleanField,
                     SelectField, SelectMultipleField, validators)
from flask.ext.wtf import Form
from flaskbb.extensions import db

def normalize_to(value, value_type, reverse=False):
    """Converts a value to a specified value type.
    Available value types are: string, integer, boolean and array.
    A boolean type is handled as 0 for false and 1 for true.

    :param value: The value which should be converted.
    :param value_type: The value_type.
    :param reverse: If the value should be converted back
    """
    if reverse:
        if value_type == 'array':
            return value
        if value_type == 'integer':
            return int(value)
        if value_type == 'float':
            return float(value)
        if value_type == 'boolean':
            return value == "1"

        # text
        return value
    else:
        if value_type == 'array':
            return value.replace(" ", "").strip(",")
        if value_type == 'integer':
            return int(value)
        if value_type == 'float':
            return float(value)
        if value_type == 'boolean':
            return 1 if value else 0

        # text
        return value

class SettingsGroup(db.Model):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)
    settings = db.relationship("Setting", lazy="dynamic", backref="group",
                               cascade="all, delete-orphan")

    def save(self):
        """Saves a settingsgroup."""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a settingsgroup."""
        db.session.delete(self)
        db.session.commit()

class Setting(db.Model):
    __tablename__ = "settings"

    key = db.Column(db.String, primary_key=True)
    _value = db.Column("value", db.String, nullable=False)
    settingsgroup = db.Column(db.String,
                              db.ForeignKey('settingsgroup.key',
                                            use_alter=True,
                                            name="fk_settingsgroup"),
                              nullable=False)

    # The name (displayed in the form)
    name = db.Column(db.String, nullable=False)

    # The description (displayed in the form)
    description = db.Column(db.String, nullable=False)

    # Available types: string, integer, float, boolean, select, selectmultiple
    value_type = db.Column(db.String, nullable=False)

    # Extra attributes like, validation things (min, max length...)
    extra = db.Column(db.PickleType)

    # Properties
    @property
    def value(self):
        return normalize_to(self._value, self.value_type)

    @value.setter
    def value(self, value):
        self._value = normalize_to(value, self.value_type, reverse=True)

    @classmethod
    def get_form(cls, group):
        """Returns a Form for all settings found in :class:`SettingsGroup`.

        :param group: The settingsgroup name. It is used to get the settings
                      which are in the specified group.
        """

        class SettingsForm(Form):
            pass

        # now parse that shit
        for setting in group.settings:
            field_validators = []

            # generate the validators
            # TODO: Do this in another function
            if "min" in setting.extra:
                # Min number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(min=setting.extra["min"])
                    )

                # Min text length validator
                elif setting.value_type in ("string", "array"):
                    field_validators.append(
                        validators.Length(min=setting.extra["min"])
                    )

            if "max" in setting.extra:
                # Max number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(max=setting.extra["max"])
                    )

                # Max text length validator
                elif setting.value_type in ("string", "array"):
                    field_validators.append(
                        validators.Length(max=setting.extra["max"])
                    )

            # Generate the fields based on value_type
            # IntegerField
            if setting.value_type == "integer":
                setattr(
                    SettingsForm, setting.key,
                    IntegerField(setting.name, validators=field_validators,
                                 description=setting.description)
                )
            # FloatField
            elif setting.value_type == "float":
                setattr(
                    SettingsForm, setting.key,
                    FloatField(setting.name, validators=field_validators,
                               description=setting.description)
                )

            # TextField
            if setting.value_type == "string":
                setattr(
                    SettingsForm, setting.key,
                    TextField(setting.name, validators=field_validators,
                              description=setting.description)
                )

            # SelectMultipleField
            if setting.value_type == "selectmultiple":
                setattr(
                    SettingsForm, setting.key,
                    SelectMultipleField(
                        setting.name,
                        choices=setting.extra['choices'],
                        description=setting.description
                    )
                )

            # SelectField
            if setting.value_type == "select":
                setattr(
                    SettingsForm, setting.key,
                    SelectField(setting.name, choices=setting.extra['choices'],
                                description=setting.description)
                )

            # BooleanField
            if setting.value_type == "boolean":
                setattr(
                    SettingsForm, setting.key,
                    BooleanField(setting.name, description=setting.description)
                )

        return SettingsForm

    @classmethod
    def get_all(cls):
        return cls.query.all()

    @classmethod
    def update(cls, settings, app=None):
        """Updates the current_app's config and stores the changes in the
        database.

        :param config: A dictionary with configuration items.
        """
        updated_settings = {}
        for key, value in settings.iteritems():
            setting = cls.query.filter(Setting.key == key.lower()).first()

            setting.value = value

            updated_settings[setting.key.upper()] = setting.value

            db.session.add(setting)
            db.session.commit()

        if app is not None:
            app.config.update(updated_settings)

    @classmethod
    def as_dict(cls, from_group=None, upper=False):
        """Returns the settings key and value as a dict.

        :param from_group: Returns only the settings from the group as a dict.
        :param upper: If upper is ``True``, the key will use upper-case
                      letters. Defaults to ``False``.
        """
        settings = {}
        result = None
        if from_group is not None:
            result = SettingsGroup.query.filter_by(key=from_group).\
                first_or_404()
            result = result.settings
        else:
            result = cls.query.all()

        for setting in result:
            if upper:
                settings[setting.key.upper()] = setting.value
            else:
                settings[setting.key] = setting.value

        return settings

    def save(self):
        """Saves a setting"""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a setting"""
        db.session.delete(self)
        db.session.commit()
<commit_msg>The PickleType is super awesome! :smile:<commit_after>from wtforms import (TextField, IntegerField, FloatField, BooleanField,
                     SelectField, SelectMultipleField, validators)
from flask.ext.wtf import Form
from flaskbb.extensions import db

class SettingsGroup(db.Model):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)
    settings = db.relationship("Setting", lazy="dynamic", backref="group",
                               cascade="all, delete-orphan")

    def save(self):
        """Saves a settingsgroup."""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a settingsgroup."""
        db.session.delete(self)
        db.session.commit()

class Setting(db.Model):
    __tablename__ = "settings"

    key = db.Column(db.String, primary_key=True)
    value = db.Column(db.PickleType, nullable=False)
    settingsgroup = db.Column(db.String,
                              db.ForeignKey('settingsgroup.key',
                                            use_alter=True,
                                            name="fk_settingsgroup"),
                              nullable=False)

    # The name (displayed in the form)
    name = db.Column(db.String, nullable=False)

    # The description (displayed in the form)
    description = db.Column(db.String, nullable=False)

    # Available types: string, integer, float, boolean, select, selectmultiple
    value_type = db.Column(db.String, nullable=False)

    # Extra attributes like, validation things (min, max length...)
    # For Select*Fields required: choices
    extra = db.Column(db.PickleType)

    @classmethod
    def get_form(cls, group):
        """Returns a Form for all settings found in :class:`SettingsGroup`.

        :param group: The settingsgroup name. It is used to get the settings
                      which are in the specified group.
        """

        class SettingsForm(Form):
            pass

        # now parse the settings in this group
        for setting in group.settings:
            field_validators = []

            # generate the validators
            if "min" in setting.extra:
                # Min number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(min=setting.extra["min"])
                    )

                # Min text length validator
                elif setting.value_type in ("string"):
                    field_validators.append(
                        validators.Length(min=setting.extra["min"])
                    )

            if "max" in setting.extra:
                # Max number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(max=setting.extra["max"])
                    )

                # Max text length validator
                elif setting.value_type in ("string"):
                    field_validators.append(
                        validators.Length(max=setting.extra["max"])
                    )

            # Generate the fields based on value_type
            # IntegerField
            if setting.value_type == "integer":
                setattr(
                    SettingsForm, setting.key,
                    IntegerField(setting.name, validators=field_validators,
                                 description=setting.description)
                )
            # FloatField
            elif setting.value_type == "float":
                setattr(
                    SettingsForm, setting.key,
                    FloatField(setting.name, validators=field_validators,
                               description=setting.description)
                )

            # TextField
            if setting.value_type == "string":
                setattr(
                    SettingsForm, setting.key,
                    TextField(setting.name, validators=field_validators,
                              description=setting.description)
                )

            # SelectMultipleField
            if setting.value_type == "selectmultiple":
                setattr(
                    SettingsForm, setting.key,
                    SelectMultipleField(
                        setting.name,
                        choices=setting.extra['choices'],
                        description=setting.description
                    )
                )

            # SelectField
            if setting.value_type == "select":
                setattr(
                    SettingsForm, setting.key,
                    SelectField(setting.name, choices=setting.extra['choices'],
                                description=setting.description)
                )

            # BooleanField
            if setting.value_type == "boolean":
                setattr(
                    SettingsForm, setting.key,
                    BooleanField(setting.name, description=setting.description)
                )

        return SettingsForm

    @classmethod
    def get_all(cls):
        return cls.query.all()

    @classmethod
    def update(cls, settings, app=None):
        """Updates the current_app's config and stores the changes in the
        database.

        :param config: A dictionary with configuration items.
        """
        updated_settings = {}
        for key, value in settings.iteritems():
            setting = cls.query.filter(Setting.key == key.lower()).first()

            setting.value = value

            updated_settings[setting.key.upper()] = setting.value

            db.session.add(setting)
            db.session.commit()

        if app is not None:
            app.config.update(updated_settings)

    @classmethod
    def as_dict(cls, from_group=None, upper=False):
        """Returns the settings key and value as a dict.

        :param from_group: Returns only the settings from the group as a dict.
        :param upper: If upper is ``True``, the key will use upper-case
                      letters. Defaults to ``False``.
        """
        settings = {}
        result = None
        if from_group is not None:
            result = SettingsGroup.query.filter_by(key=from_group).\
                first_or_404()
            result = result.settings
        else:
            result = cls.query.all()

        for setting in result:
            if upper:
                settings[setting.key.upper()] = setting.value
            else:
                settings[setting.key] = setting.value

        return settings

    def save(self):
        """Saves a setting"""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a setting"""
        db.session.delete(self)
        db.session.commit()
<|endoftext|>


<commit_before>from wtforms import (TextField, IntegerField, FloatField, BooleanField,
                     SelectField, SelectMultipleField, validators)
from flask.ext.wtf import Form
from flaskbb.extensions import db

class SettingsGroup(db.Model):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)
    settings = db.relationship("Setting", lazy="dynamic", backref="group",
                               cascade="all, delete-orphan")

    def save(self):
        """Saves a settingsgroup."""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a settingsgroup."""
        db.session.delete(self)
        db.session.commit()

class Setting(db.Model):
    __tablename__ = "settings"

    key = db.Column(db.String, primary_key=True)
    value = db.Column(db.PickleType, nullable=False)
    settingsgroup = db.Column(db.String,
                              db.ForeignKey('settingsgroup.key',
                                            use_alter=True,
                                            name="fk_settingsgroup"),
                              nullable=False)

    # The name (displayed in the form)
    name = db.Column(db.String, nullable=False)

    # The description (displayed in the form)
    description = db.Column(db.String, nullable=False)

    # Available types: string, integer, float, boolean, select, selectmultiple
    value_type = db.Column(db.String, nullable=False)

    # Extra attributes like, validation things (min, max length...)
    # For Select*Fields required: choices
    extra = db.Column(db.PickleType)

    @classmethod
    def get_form(cls, group):
        """Returns a Form for all settings found in :class:`SettingsGroup`.

        :param group: The settingsgroup name. It is used to get the settings
                      which are in the specified group.
        """

        class SettingsForm(Form):
            pass

        # now parse the settings in this group
        for setting in group.settings:
            field_validators = []

            # generate the validators
            if "min" in setting.extra:
                # Min number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(min=setting.extra["min"])
                    )

                # Min text length validator
                elif setting.value_type in ("string"):
                    field_validators.append(
                        validators.Length(min=setting.extra["min"])
                    )

            if "max" in setting.extra:
                # Max number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(max=setting.extra["max"])
                    )

                # Max text length validator
                elif setting.value_type in ("string"):
                    field_validators.append(
                        validators.Length(max=setting.extra["max"])
                    )

            # Generate the fields based on value_type
            # IntegerField
            if setting.value_type == "integer":
                setattr(
                    SettingsForm, setting.key,
                    IntegerField(setting.name, validators=field_validators,
                                 description=setting.description)
                )
            # FloatField
            elif setting.value_type == "float":
                setattr(
                    SettingsForm, setting.key,
                    FloatField(setting.name, validators=field_validators,
                               description=setting.description)
                )

            # TextField
            if setting.value_type == "string":
                setattr(
                    SettingsForm, setting.key,
                    TextField(setting.name, validators=field_validators,
                              description=setting.description)
                )

            # SelectMultipleField
            if setting.value_type == "selectmultiple":
                setattr(
                    SettingsForm, setting.key,
                    SelectMultipleField(
                        setting.name,
                        choices=setting.extra['choices'],
                        description=setting.description
                    )
                )

            # SelectField
            if setting.value_type == "select":
                setattr(
                    SettingsForm, setting.key,
                    SelectField(setting.name, choices=setting.extra['choices'],
                                description=setting.description)
                )

            # BooleanField
            if setting.value_type == "boolean":
                setattr(
                    SettingsForm, setting.key,
                    BooleanField(setting.name, description=setting.description)
                )

        return SettingsForm

    @classmethod
    def get_all(cls):
        return cls.query.all()

    @classmethod
    def update(cls, settings, app=None):
        """Updates the current_app's config and stores the changes in the
        database.

        :param config: A dictionary with configuration items.
        """
        updated_settings = {}
        for key, value in settings.iteritems():
            setting = cls.query.filter(Setting.key == key.lower()).first()

            setting.value = value

            updated_settings[setting.key.upper()] = setting.value

            db.session.add(setting)
            db.session.commit()

        if app is not None:
            app.config.update(updated_settings)

    @classmethod
    def as_dict(cls, from_group=None, upper=False):
        """Returns the settings key and value as a dict.

        :param from_group: Returns only the settings from the group as a dict.
        :param upper: If upper is ``True``, the key will use upper-case
                      letters. Defaults to ``False``.
        """
        settings = {}
        result = None
        if from_group is not None:
            result = SettingsGroup.query.filter_by(key=from_group).\
                first_or_404()
            result = result.settings
        else:
            result = cls.query.all()

        for setting in result:
            if upper:
                settings[setting.key.upper()] = setting.value
            else:
                settings[setting.key] = setting.value

        return settings

    def save(self):
        """Saves a setting"""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a setting"""
        db.session.delete(self)
        db.session.commit()
<commit_msg>Coercing the data for the select fields now.<commit_after>from wtforms import (TextField, IntegerField, FloatField, BooleanField,
                     SelectField, SelectMultipleField, validators)
from flask.ext.wtf import Form
from flaskbb.extensions import db

class SettingsGroup(db.Model):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)
    settings = db.relationship("Setting", lazy="dynamic", backref="group",
                               cascade="all, delete-orphan")

    def save(self):
        """Saves a settingsgroup."""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a settingsgroup."""
        db.session.delete(self)
        db.session.commit()

class Setting(db.Model):
    __tablename__ = "settings"

    key = db.Column(db.String, primary_key=True)
    value = db.Column(db.PickleType, nullable=False)
    settingsgroup = db.Column(db.String,
                              db.ForeignKey('settingsgroup.key',
                                            use_alter=True,
                                            name="fk_settingsgroup"),
                              nullable=False)

    # The name (displayed in the form)
    name = db.Column(db.String, nullable=False)

    # The description (displayed in the form)
    description = db.Column(db.String, nullable=False)

    # Available types: string, integer, float, boolean, select, selectmultiple
    value_type = db.Column(db.String, nullable=False)

    # Extra attributes like, validation things (min, max length...)
    # For Select*Fields required: choices
    extra = db.Column(db.PickleType)

    @classmethod
    def get_form(cls, group):
        """Returns a Form for all settings found in :class:`SettingsGroup`.

        :param group: The settingsgroup name. It is used to get the settings
                      which are in the specified group.
        """

        class SettingsForm(Form):
            pass

        # now parse the settings in this group
        for setting in group.settings:
            field_validators = []

            # generate the validators
            if "min" in setting.extra:
                # Min number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(min=setting.extra["min"])
                    )

                # Min text length validator
                elif setting.value_type in ("string"):
                    field_validators.append(
                        validators.Length(min=setting.extra["min"])
                    )

            if "max" in setting.extra:
                # Max number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(max=setting.extra["max"])
                    )

                # Max text length validator
                elif setting.value_type in ("string"):
                    field_validators.append(
                        validators.Length(max=setting.extra["max"])
                    )

            # Generate the fields based on value_type
            # IntegerField
            if setting.value_type == "integer":
                setattr(
                    SettingsForm, setting.key,
                    IntegerField(setting.name, validators=field_validators,
                                 description=setting.description)
                )
            # FloatField
            elif setting.value_type == "float":
                setattr(
                    SettingsForm, setting.key,
                    FloatField(setting.name, validators=field_validators,
                               description=setting.description)
                )

            # TextField
            if setting.value_type == "string":
                setattr(
                    SettingsForm, setting.key,
                    TextField(setting.name, validators=field_validators,
                              description=setting.description)
                )

            # SelectMultipleField
            if setting.value_type == "selectmultiple":
                # if no coerce is found, it will fallback to unicode
                if "coerce" in setting.extra:
                    coerce_to = setting.extra['coerce']
                else:
                    coerce_to = unicode

                setattr(
                    SettingsForm, setting.key,
                    SelectMultipleField(
                        setting.name,
                        choices=setting.extra['choices'](),
                        coerce=coerce_to,
                        description=setting.description
                    )
                )

            # SelectField
            if setting.value_type == "select":
                # if no coerce is found, it will fallback to unicode
                if "coerce" in setting.extra:
                    coerce_to = setting.extra['coerce']
                else:
                    coerce_to = unicode

                setattr(
                    SettingsForm, setting.key,
                    SelectField(
                        setting.name,
                        coerce=coerce_to,
                        choices=setting.extra['choices'](),
                        description=setting.description)
                )

            # BooleanField
            if setting.value_type == "boolean":
                setattr(
                    SettingsForm, setting.key,
                    BooleanField(setting.name, description=setting.description)
                )

        return SettingsForm

    @classmethod
    def get_all(cls):
        return cls.query.all()

    @classmethod
    def update(cls, settings, app=None):
        """Updates the current_app's config and stores the changes in the
        database.

        :param config: A dictionary with configuration items.
        """
        updated_settings = {}
        for key, value in settings.iteritems():
            setting = cls.query.filter(Setting.key == key.lower()).first()

            setting.value = value

            updated_settings[setting.key.upper()] = setting.value

            db.session.add(setting)
            db.session.commit()

        if app is not None:
            app.config.update(updated_settings)

    @classmethod
    def as_dict(cls, from_group=None, upper=False):
        """Returns the settings key and value as a dict.

        :param from_group: Returns only the settings from the group as a dict.
        :param upper: If upper is ``True``, the key will use upper-case
                      letters. Defaults to ``False``.
        """
        settings = {}
        result = None
        if from_group is not None:
            result = SettingsGroup.query.filter_by(key=from_group).\
                first_or_404()
            result = result.settings
        else:
            result = cls.query.all()

        for setting in result:
            if upper:
                settings[setting.key.upper()] = setting.value
            else:
                settings[setting.key] = setting.value

        return settings

    def save(self):
        """Saves a setting"""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a setting"""
        db.session.delete(self)
        db.session.commit()
<|endoftext|>