<commit_before>"""
Define the Symbol class

Separate file because it depends on many other files, so we try to simplify
the circular dependencies.
"""

from __future__ import annotations  # Allows forward declarations
from typing import TYPE_CHECKING, Any, Union, Optional, Type
# ads.Connection relies on structs.AdsSymbol (but type hints only), so use
# this if to only include it when type hinting (False during execution)
if TYPE_CHECKING:
    from .ads import Connection
import re

from .pyads_ex import adsGetSymbolInfo
from . import constants
# We want to access all constants, so use package notation

class AdsSymbol:
    """Object that points to an ADS variable

    Contains index group, index offset, name, symbol type, comment of ADS
    symbol. Also remembers a reference to a Connection to be able to
    read/write directly.

    :param index_group: Index group of symbol
    :param index_offset: Index offset of symbol
    :param name: Name of symbol
    :param symtype: String representation of symbol type
    :param comment: Comment of symbol

    """

    def __init__(self,
                 plc: Connection,
                 name: Optional[str] = None,
                 index_group: Optional[int] = None,
                 index_offset: Optional[int] = None,
                 symtype: Optional[Union[Type, str]] = None,
                 comment=None):
        """Create AdsSymbol instance

        Specify either the variable name or the index_group _and_
        index_offset so the symbol can be located.
        If the name was specified but not all other attributes were,
        the other attributes will be looked up from the connection.
        `symtype` can be a PLCTYPE_* constant or a string representing a PLC
        type (e.g. 'LREAL').

        The virtual property `value` can be used to read from and write to
        the symbol.

        :param plc: Connection instance
        :param name:
        :param index_group:
        :param index_offset:
        :param symtype:
        :param comment:

        """
        self._plc = plc

        do_lookup = True

        if index_group is None or index_offset is None or symtype is None:
            if name is None:
                raise ValueError('Please specify either `name`, or '
                                 '`index_group`, `index_offset` and '
                                 'symtype')
            else:
                self.name = name
        else:
            self.index_offset = index_offset
            self.index_group = index_group
            self.name = name
            self.comment = comment
            do_lookup = False  # Have what we need already

        if do_lookup:
            info = adsGetSymbolInfo(self._plc.port, self._plc.ams_addr,
                                    name)

            self.index_group = info.iGroup
            self.index_offset = info.iOffs
            self.comment = comment
            symtype = info.type_name  # Type name, e.g. 'LREAL'

        if isinstance(symtype, str):
            self.type_name = symtype  # Store human-readable type name
            self.symtype = self.get_type_from_str(symtype)
        else:
            self.type_name = symtype.__class__  # Try to find human-readable
            self.symtype = symtype

    def read(self) -> Any:
        """Read the current value of this symbol"""
        return self._plc.read(self.index_group, self.index_offset,
                              self.symtype)

    def write(self, new_value: Any):
        """Write a new value to the symbol"""
        if self._handle:
            return self._plc.write_by_name(self.name, new_value, self.symtype,
                                           handle=self._handle)
        return self._plc.write(self.index_group, self.index_offset,
                               new_value, self.symtype)

    @property
    def value(self):
        return self.read()

    @value.setter
    def value(self, new_value):
        self.write(new_value)

    def __repr__(self):
        """Debug string"""
        t = type(self)
        return '<{}.{} object at {}, name: {}, type: {}>'.format(
            t.__module__, t.__qualname__, hex(id(self)),
            self.name, self.type_name)

    @staticmethod
    def get_type_from_str(type_str: str) -> Optional[Type]:
        """Get PLCTYPE_* from PLC name string

        If PLC name could not be mapped, return None. This is done on
        purpose to prevent a program from crashing when an unusable symbol
        is found. Instead, exceptions will be thrown when this unmapped
        symbol is read/written.
        """

        # If simple scalar
        plc_name = 'PLCTYPE_' + type_str
        if hasattr(constants, plc_name):
            # Map e.g. 'LREAL' to 'PLCTYPE_LREAL' directly based on the name
            return getattr(constants, plc_name)

        # If array/matrix (an 1D array is also called a matrix)
        reg_match = re.match(r'matrix_(\d+)_(.*)_T', type_str)
        if reg_match is not None:

            groups = reg_match.groups()
            size = int(groups[0])
            scalar_type_str = groups[1]

            if scalar_type_str in constants.PLC_ARRAY_MAP:
                return constants.PLC_ARRAY_MAP[scalar_type_str](size)

        # We allow unmapped types at this point - Instead we will throw  an
        # error when they are being addressed

        return None
<commit_msg>Added notification management<commit_after>"""
Define the Symbol class

Separate file because it depends on many other files, so we try to simplify
the circular dependencies.
"""

from __future__ import annotations  # Allows forward declarations
from typing import TYPE_CHECKING, Any, Union, Optional, Type, List, Tuple, \
    Callable
# ads.Connection relies on structs.AdsSymbol (but type hints only), so use
# this if to only include it when type hinting (False during execution)
if TYPE_CHECKING:
    from .ads import Connection
import re
from ctypes import sizeof,

from .pyads_ex import adsGetSymbolInfo
from .structs import NotificationAttrib
from . import constants  # To access all constants, use package notation

class AdsSymbol:
    """Object that points to an ADS variable

    Contains index group, index offset, name, symbol type, comment of ADS
    symbol. Also remembers a reference to a Connection to be able to
    read/write directly.

    :param index_group: Index group of symbol
    :param index_offset: Index offset of symbol
    :param name: Name of symbol
    :param symtype: String representation of symbol type
    :param comment: Comment of symbol

    """

    def __init__(self,
                 plc: Connection,
                 name: Optional[str] = None,
                 index_group: Optional[int] = None,
                 index_offset: Optional[int] = None,
                 symtype: Optional[Union[Type, str]] = None,
                 comment=None):
        """Create AdsSymbol instance

        Specify either the variable name or the index_group _and_
        index_offset so the symbol can be located.
        If the name was specified but not all other attributes were,
        the other attributes will be looked up from the connection.
        `symtype` can be a PLCTYPE_* constant or a string representing a PLC
        type (e.g. 'LREAL').

        The virtual property `value` can be used to read from and write to
        the symbol.

        :param plc: Connection instance
        :param name:
        :param index_group:
        :param index_offset:
        :param symtype:
        :param comment:

        """
        self._plc = plc
        self._handles_list: List[Tuple[int, int]] = []  # Notification handles

        do_lookup = True

        if index_group is None or index_offset is None or symtype is None:
            if name is None:
                raise ValueError('Please specify either `name`, or '
                                 '`index_group`, `index_offset` and '
                                 'symtype')
            else:
                self.name = name
        else:
            self.index_offset = index_offset
            self.index_group = index_group
            self.name = name
            self.comment = comment
            do_lookup = False  # Have what we need already

        if do_lookup:
            info = adsGetSymbolInfo(self._plc.port, self._plc.ams_addr,
                                    name)

            self.index_group = info.iGroup
            self.index_offset = info.iOffs
            self.comment = comment
            symtype = info.type_name  # Type name, e.g. 'LREAL'

        if isinstance(symtype, str):
            self.type_name = symtype  # Store human-readable type name
            self.symtype = self.get_type_from_str(symtype)
        else:
            self.type_name = symtype.__class__  # Try to find human-readable
            self.symtype = symtype

    def read(self) -> Any:
        """Read the current value of this symbol"""
        return self._plc.read(self.index_group, self.index_offset,
                              self.symtype)

    def write(self, new_value: Any):
        """Write a new value to the symbol"""
        return self._plc.write(self.index_group, self.index_offset,
                               new_value, self.symtype)

    @property
    def value(self):
        return self.read()

    @value.setter
    def value(self, new_value):
        self.write(new_value)

    def __repr__(self):
        """Debug string"""
        t = type(self)
        return '<{}.{} object at {}, name: {}, type: {}>'.format(
            t.__module__, t.__qualname__, hex(id(self)),
            self.name, self.type_name)

    def __del__(self):
        """Destructor"""
        self.clear_device_notifications()

    def add_device_notification(
            self,
            callback: Callable,
            attr: Optional[NotificationAttrib] = None,
            user_handle:  Optional[int] = None
    ) -> Optional[Tuple[int, int]]:
        """Add on-change callback to symbol

        See Connection.add_device_notification(...).

        When `attr` is omitted, the default will be used.

        The notification handles are returned but also stored locally. When
        this symbol is destructed any notifications will be freed up
        automatically.
        """

        if attr is None:
            attr = NotificationAttrib(length=sizeof(self.symtype))

        handles = self._plc.add_device_notification(
                (self.index_group, self.index_offset),
                attr,
                callback,
                user_handle
        )

        self._handles_list.append(handles)

        return handles

    def clear_device_notifications(self):
        """Remove all registered notifications"""
        if self._handles_list:
            for handles in self._handles_list:
                self._plc.del_device_notification(*handles)
            self._handles_list = []  # Clear the list

    def del_device_notification(self, handles: Tuple[int, int]):
        """Remove a single device notification by handles"""
        if handles in self._handles_list:
            self._plc.del_device_notification(*handles)
            self._handles_list.remove(handles)

    @staticmethod
    def get_type_from_str(type_str: str) -> Optional[Type]:
        """Get PLCTYPE_* from PLC name string

        If PLC name could not be mapped, return None. This is done on
        purpose to prevent a program from crashing when an unusable symbol
        is found. Instead, exceptions will be thrown when this unmapped
        symbol is read/written.
        """

        # If simple scalar
        plc_name = 'PLCTYPE_' + type_str
        if hasattr(constants, plc_name):
            # Map e.g. 'LREAL' to 'PLCTYPE_LREAL' directly based on the name
            return getattr(constants, plc_name)

        # If array/matrix (an 1D array is also called a matrix)
        reg_match = re.match(r'matrix_(\d+)_(.*)_T', type_str)
        if reg_match is not None:

            groups = reg_match.groups()
            size = int(groups[0])
            scalar_type_str = groups[1]

            if scalar_type_str in constants.PLC_ARRAY_MAP:
                return constants.PLC_ARRAY_MAP[scalar_type_str](size)

        # We allow unmapped types at this point - Instead we will throw  an
        # error when they are being addressed

        return None
<|endoftext|>


<commit_before>#Copyright (C) 2008 Codethink Ltd

#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License version 2 as published by the Free Software Foundation.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#You should have received a copy of the GNU Lesser General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import dbus
from dbus.proxies import Interface
from dbus.exceptions import *

import interfaces

__all__ = [
           "AccessibleObjectNoLongerExists",
           "AccessibleObjectNotAvailable",
           "Enum",
           "BaseProxy",
          ]

class AccessibleObjectNoLongerExists(Exception):
        pass

class AccessibleObjectNotAvailable(Exception):
        pass

#------------------------------------------------------------------------------

class Enum(int):
        def __str__(self):
                return self._enum_lookup[int(self)]

#------------------------------------------------------------------------------

class BaseProxyMeta(type):
        def __new__(meta, *args, **kwargs):
                cls = type.__new__(meta, *args, **kwargs)

                queryable_interfaces = {
                        'Accessible':interfaces.ATSPI_ACCESSIBLE,
                        'Action':interfaces.ATSPI_ACTION,
                        'Application':interfaces.ATSPI_APPLICATION,
                        'Collection':interfaces.ATSPI_COLLECTION,
                        'Component':interfaces.ATSPI_COMPONENT,
                        'Desktop':interfaces.ATSPI_DESKTOP,
                        'Document':interfaces.ATSPI_DOCUMENT,
                        'EditableText':interfaces.ATSPI_EDITABLE_TEXT,
                        'Hypertext':interfaces.ATSPI_HYPERTEXT,
                        'Hyperlink':interfaces.ATSPI_HYPERLINK,
                        'Image':interfaces.ATSPI_IMAGE,
                        'Selection':interfaces.ATSPI_SELECTION,
                        'StreamableContent':interfaces.ATSPI_STREAMABLE_CONTENT,
                        'Table':interfaces.ATSPI_TABLE,
                        'Text':interfaces.ATSPI_TEXT,
                        'Value':interfaces.ATSPI_VALUE,
                }

                def return_query(interface):
                        def new_query(self):
                                return self.queryInterface(interface)
                        return new_query

                for interface in queryable_interfaces.keys():
                        name = 'query%s' % interface
                        setattr(cls, name, return_query(queryable_interfaces[interface]))

                return cls

#------------------------------------------------------------------------------

class BaseProxy(object):
        """
        The base D-Bus proxy for a remote object that implements one or more
        of the AT-SPI interfaces.
        """

        __metaclass__ = BaseProxyMeta

        def __init__(self, app_name, acc_path, cache, interface, dbus_object=None):
                """
                Create a D-Bus Proxy for an ATSPI interface.

                cache - ApplicationCache, where the cached data for the accessible can be obtained.
                app_name - D-Bus bus name of the application this accessible belongs to.
                acc_path - D-Bus object path of the server side accessible object.
                parent - Parent accessible.
                dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls.
                """
                self._cache = cache
                self._app_name = app_name
                self._acc_path = acc_path
                self._dbus_interface = interface

                if not dbus_object:
                        dbus_object = cache.connection.get_object(self._app_name,
                                                                  self._acc_path,
                                                                  introspect=False)
                self._dbus_object = dbus_object

                self._pgetter = self.get_dbus_method("Get",
                                                     dbus_interface="org.freedesktop.DBus.Properties")
                self._psetter = self.get_dbus_method("Set",
                                                     dbus_interface="org.freedesktop.DBus.Properties")

        def __str__(self):
                    try:
                              return '[%s | %s]' % (self.getRoleName(), self.name)
                    except Exception:
                              return '[DEAD]'

        def __eq__(self, other):
                if other is None:
                        return False
                try:
                        if self._app_name == other._app_name and \
                           self._acc_path == other._acc_path:
                                return True
                        else:
                                return False
                except AttributeError:
                        return False

        def __ne__(self, other):
                return not self.__eq__(other)

        def __hash__(self):
                return hash(self._app_name + self._acc_path)

        def get_dbus_method(self, *args, **kwargs):
                method =  self._dbus_object.get_dbus_method(*args, **kwargs)

                def dbus_method_func(*iargs, **ikwargs):
                        # TODO Need to throw an AccessibleObjectNoLongerExists exception
                        # on D-Bus error of the same type.
                        try:
                                return method(*iargs, **ikwargs)
                        except UnknownMethodException, e:
                                raise NotImplementedError(e)
                        except DBusException, e:
                                raise LookupError(e)

                return dbus_method_func

        @property
        def cached_data(self):
                try:
                        return self._cache.get_cache_data(self._app_name, self._acc_path)
                except KeyError:
                        raise AccessibleObjectNoLongerExists, \
                                'Cache data cannot be found for path %s in app %s' % (self._acc_path, self._app_name)

        @property
        def interfaces(self):
                return self.cached_data.interfaces

        def queryInterface(self, interface):
                """
                Gets a different accessible interface for this object
                or raises a NotImplemented error if the given interface
                is not supported.
                """
                if interface in self.interfaces:
                        return self._cache.create_accessible(self._app_name,
                                                             self._acc_path,
                                                             interface,
                                                             dbus_object=self._dbus_object)
                else:
                        raise NotImplementedError(
                                "%s not supported by accessible object at path %s"
                                % (interface, self._acc_path))

#END----------------------------------------------------------------------------
<commit_msg>2009-04-16  Mike Gorse  <mgorse@novell.com><commit_after>#Copyright (C) 2008 Codethink Ltd

#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU Lesser General Public
#License version 2 as published by the Free Software Foundation.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#You should have received a copy of the GNU Lesser General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import dbus
from dbus.proxies import Interface
from dbus.exceptions import *

import interfaces

__all__ = [
           "AccessibleObjectNoLongerExists",
           "AccessibleObjectNotAvailable",
           "Enum",
           "BaseProxy",
          ]

class AccessibleObjectNoLongerExists(Exception):
        pass

class AccessibleObjectNotAvailable(Exception):
        pass

#------------------------------------------------------------------------------

class Enum(uint):
        def __str__(self):
                return self._enum_lookup[int(self)]

#------------------------------------------------------------------------------

class BaseProxyMeta(type):
        def __new__(meta, *args, **kwargs):
                cls = type.__new__(meta, *args, **kwargs)

                queryable_interfaces = {
                        'Accessible':interfaces.ATSPI_ACCESSIBLE,
                        'Action':interfaces.ATSPI_ACTION,
                        'Application':interfaces.ATSPI_APPLICATION,
                        'Collection':interfaces.ATSPI_COLLECTION,
                        'Component':interfaces.ATSPI_COMPONENT,
                        'Desktop':interfaces.ATSPI_DESKTOP,
                        'Document':interfaces.ATSPI_DOCUMENT,
                        'EditableText':interfaces.ATSPI_EDITABLE_TEXT,
                        'Hypertext':interfaces.ATSPI_HYPERTEXT,
                        'Hyperlink':interfaces.ATSPI_HYPERLINK,
                        'Image':interfaces.ATSPI_IMAGE,
                        'Selection':interfaces.ATSPI_SELECTION,
                        'StreamableContent':interfaces.ATSPI_STREAMABLE_CONTENT,
                        'Table':interfaces.ATSPI_TABLE,
                        'Text':interfaces.ATSPI_TEXT,
                        'Value':interfaces.ATSPI_VALUE,
                }

                def return_query(interface):
                        def new_query(self):
                                return self.queryInterface(interface)
                        return new_query

                for interface in queryable_interfaces.keys():
                        name = 'query%s' % interface
                        setattr(cls, name, return_query(queryable_interfaces[interface]))

                return cls

#------------------------------------------------------------------------------

class BaseProxy(object):
        """
        The base D-Bus proxy for a remote object that implements one or more
        of the AT-SPI interfaces.
        """

        __metaclass__ = BaseProxyMeta

        def __init__(self, app_name, acc_path, cache, interface, dbus_object=None):
                """
                Create a D-Bus Proxy for an ATSPI interface.

                cache - ApplicationCache, where the cached data for the accessible can be obtained.
                app_name - D-Bus bus name of the application this accessible belongs to.
                acc_path - D-Bus object path of the server side accessible object.
                parent - Parent accessible.
                dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls.
                """
                self._cache = cache
                self._app_name = app_name
                self._acc_path = acc_path
                self._dbus_interface = interface

                if not dbus_object:
                        dbus_object = cache.connection.get_object(self._app_name,
                                                                  self._acc_path,
                                                                  introspect=False)
                self._dbus_object = dbus_object

                self._pgetter = self.get_dbus_method("Get",
                                                     dbus_interface="org.freedesktop.DBus.Properties")
                self._psetter = self.get_dbus_method("Set",
                                                     dbus_interface="org.freedesktop.DBus.Properties")

        def __str__(self):
                    try:
                              return '[%s | %s]' % (self.getRoleName(), self.name)
                    except Exception:
                              return '[DEAD]'

        def __eq__(self, other):
                if other is None:
                        return False
                try:
                        if self._app_name == other._app_name and \
                           self._acc_path == other._acc_path:
                                return True
                        else:
                                return False
                except AttributeError:
                        return False

        def __ne__(self, other):
                return not self.__eq__(other)

        def __hash__(self):
                return hash(self._app_name + self._acc_path)

        def get_dbus_method(self, *args, **kwargs):
                method =  self._dbus_object.get_dbus_method(*args, **kwargs)

                def dbus_method_func(*iargs, **ikwargs):
                        # TODO Need to throw an AccessibleObjectNoLongerExists exception
                        # on D-Bus error of the same type.
                        try:
                                return method(*iargs, **ikwargs)
                        except UnknownMethodException, e:
                                raise NotImplementedError(e)
                        except DBusException, e:
                                raise LookupError(e)

                return dbus_method_func

        @property
        def cached_data(self):
                try:
                        return self._cache.get_cache_data(self._app_name, self._acc_path)
                except KeyError:
                        raise AccessibleObjectNoLongerExists, \
                                'Cache data cannot be found for path %s in app %s' % (self._acc_path, self._app_name)

        @property
        def interfaces(self):
                return self.cached_data.interfaces

        def queryInterface(self, interface):
                """
                Gets a different accessible interface for this object
                or raises a NotImplemented error if the given interface
                is not supported.
                """
                if interface in self.interfaces:
                        return self._cache.create_accessible(self._app_name,
                                                             self._acc_path,
                                                             interface,
                                                             dbus_object=self._dbus_object)
                else:
                        raise NotImplementedError(
                                "%s not supported by accessible object at path %s"
                                % (interface, self._acc_path))

#END----------------------------------------------------------------------------
<|endoftext|>


<commit_before># -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2013 SF Isle of Man Limited
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.

import os
import logging
from flask import Flask, url_for, session, request, render_template, flash
from flask.ext.login import current_user
from flask.ext.heroku import Heroku
from flask.ext.babel import lazy_gettext

from pybossa import default_settings as settings
from pybossa.extensions import (signer, mail, login_manager, sentinel,
                                facebook, twitter, google, misaka,
                                babel, gravatar, uploader)
from pybossa.ratelimit import get_view_rate_limit

from raven.contrib.flask import Sentry
from pybossa.model import db
from pybossa import model

def create_app(theme='default'):
    template_folder = os.path.join('themes', theme, 'templates')
    static_folder = os.path.join('themes', theme, 'static')
    app = Flask(__name__, template_folder=template_folder,
                static_folder=static_folder)
    if 'DATABASE_URL' in os.environ:  # pragma: no cover
        heroku = Heroku(app)
    configure_app(app)
    setup_uploader(app)
    setup_error_email(app)
    setup_logging(app)
    setup_login_manager(app)
    login_manager.setup_app(app)
    setup_babel(app)
    setup_markdown(app)
    # Set up Gravatar for users
    setup_gravatar(app)
    #gravatar = Gravatar(app, size=100, rating='g', default='mm',
                        #force_default=False, force_lower=False)
    setup_db(app)
    mail.init_app(app)
    sentinel.init_app(app)
    signer.init_app(app)
    if app.config.get('SENTRY_DSN'): # pragma: no cover
        sentr = Sentry(app)
    setup_blueprints(app)
    setup_hooks(app)
    setup_error_handlers(app)
    setup_social_networks(app)
    setup_jinja(app)
    setup_geocoding(app)
    #toolbar.init_app(app)
    return app

def configure_app(app):
    app.config.from_object(settings)
    app.config.from_envvar('PYBOSSA_SETTINGS', silent=True)
    # parent directory
    if not os.environ.get('PYBOSSA_SETTINGS'): # pragma: no cover
        here = os.path.dirname(os.path.abspath(__file__))
        config_path = os.path.join(os.path.dirname(here), 'settings_local.py')
        if os.path.exists(config_path): # pragma: no cover
            app.config.from_pyfile(config_path)
    # Override DB in case of testing
    if app.config.get('SQLALCHEMY_DATABASE_TEST_URI'):
        app.config['SQLALCHEMY_DATABASE_URI'] = app.config['SQLALCHEMY_DATABASE_TEST_URI']

def setup_uploader(app):
    global uploader
    if app.config.get('UPLOAD_METHOD') == 'local':
        from pybossa.uploader.local import LocalUploader
        uploader = LocalUploader()
        uploader.init_app(app)
    if app.config.get('UPLOAD_METHOD') == 'rackspace':
        from pybossa.uploader.rackspace import RackspaceUploader
        uploader = RackspaceUploader()
        app.url_build_error_handlers.append(uploader.external_url_handler)
        uploader.init_app(app)

def setup_markdown(app):
    misaka.init_app(app)

def setup_db(app):
    db.app = app
    db.init_app(app)

def setup_gravatar(app):
    gravatar.init_app(app)

from logging.handlers import SMTPHandler
def setup_error_email(app):
    ADMINS = app.config.get('ADMINS', '')
    if not app.debug and ADMINS: # pragma: no cover
        mail_handler = SMTPHandler('127.0.0.1',
                                   'server-error@no-reply.com',
                                   ADMINS, 'error')
        mail_handler.setLevel(logging.ERROR)
        app.logger.addHandler(mail_handler)

from logging.handlers import RotatingFileHandler
from logging import Formatter
def setup_logging(app):
    log_file_path = app.config.get('LOG_FILE')
    log_level = app.config.get('LOG_LEVEL', logging.WARN)
    if log_file_path: # pragma: no cover
        file_handler = RotatingFileHandler(log_file_path)
        file_handler.setFormatter(Formatter(
            '%(name)s:%(levelname)s:[%(asctime)s] %(message)s '
            '[in %(pathname)s:%(lineno)d]'
            ))
        file_handler.setLevel(log_level)
        app.logger.addHandler(file_handler)
        logger = logging.getLogger('pybossa')
        logger.setLevel(log_level)
        logger.addHandler(file_handler)

def setup_login_manager(app):
    login_manager.login_view = 'account.signin'
    login_manager.login_message = u"Please sign in to access this page."
    @login_manager.user_loader
    def load_user(username):
        return db.session.query(model.user.User).filter_by(name=username).first()

# Configure theme
try: # pragma: no cover
    # First with local settings
    import settings_local
    theme = settings_local.THEME
except:
    # Otherwise try with default theme
    theme = settings.THEME

def setup_babel(app):
    """Return babel handler."""
    babel.init_app(app)

    @babel.localeselector
    def get_locale():
        if current_user.is_authenticated():
            lang = current_user.locale
        else:
            lang = session.get('lang',
                               request.accept_languages.best_match(app.config['LOCALES']))
        if lang is None:
            lang = 'en'
        return lang
    return babel
#
def setup_blueprints(app):
    """Configure blueprints."""
    from pybossa.api import blueprint as api
    from pybossa.view.account import blueprint as account
    from pybossa.view.applications import blueprint as applications
    from pybossa.view.admin import blueprint as admin
    from pybossa.view.leaderboard import blueprint as leaderboard
    from pybossa.view.stats import blueprint as stats
    from pybossa.view.help import blueprint as help
    from pybossa.view.home import blueprint as home
    from pybossa.view.uploads import blueprint as uploads

    blueprints = [{'handler': home, 'url_prefix': '/'},
                  {'handler': api,  'url_prefix': '/api'},
                  {'handler': account, 'url_prefix': '/account'},
                  {'handler': applications, 'url_prefix': '/app'},
                  {'handler': admin, 'url_prefix': '/admin'},
                  {'handler': leaderboard, 'url_prefix': '/leaderboard'},
                  {'handler': help, 'url_prefix': '/help'},
                  {'handler': stats, 'url_prefix': '/stats'},
                  {'handler': uploads, 'url_prefix': '/uploads'},
                  ]

    for bp in blueprints:
        app.register_blueprint(bp['handler'], url_prefix=bp['url_prefix'])

def setup_social_networks(app):
    try:  # pragma: no cover
        if (app.config['TWITTER_CONSUMER_KEY'] and
                app.config['TWITTER_CONSUMER_SECRET']):
            twitter.init_app(app)
            from pybossa.view.twitter import blueprint as twitter_bp
            app.register_blueprint(twitter_bp, url_prefix='/twitter')
    except Exception as inst:  # pragma: no cover
        print type(inst)
        print inst.args
        print inst
        print "Twitter signin disabled"

    # Enable Facebook if available
    try:  # pragma: no cover
        if (app.config['FACEBOOK_APP_ID'] and app.config['FACEBOOK_APP_SECRET']):
            facebook.init_app(app)
            from pybossa.view.facebook import blueprint as facebook_bp
            app.register_blueprint(facebook_bp, url_prefix='/facebook')
    except Exception as inst: # pragma: no cover
        print type(inst)
        print inst.args
        print inst
        print "Facebook signin disabled"

    # Enable Google if available
    try:  # pragma: no cover
        if (app.config['GOOGLE_CLIENT_ID'] and app.config['GOOGLE_CLIENT_SECRET']):
            google.init_app(app)
            from pybossa.view.google import blueprint as google_bp
            app.register_blueprint(google_bp, url_prefix='/google')
    except Exception as inst:  # pragma: no cover
        print type(inst)
        print inst.args
        print inst
        print "Google signin disabled"

def setup_geocoding(app):
    # Check if app stats page can generate the map
    geolite = app.root_path + '/../dat/GeoLiteCity.dat'
    if not os.path.exists(geolite):  # pragma: no cover
        app.config['GEO'] = False
        print("GeoLiteCity.dat file not found")
        print("App page stats web map disabled")
    else:  # pragma: no cover
        app.config['GEO'] = True

def url_for_other_page(page):
    args = request.view_args.copy()
    args['page'] = page
    return url_for(request.endpoint, **args)

def setup_jinja(app):
    app.jinja_env.globals['url_for_other_page'] = url_for_other_page

def setup_error_handlers(app):
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404

    @app.errorhandler(500)
    def server_error(e):  # pragma: no cover
        return render_template('500.html'), 500

    @app.errorhandler(403)
    def forbidden(e):
        return render_template('403.html'), 403

    @app.errorhandler(401)
    def unauthorized(e):
        return render_template('401.html'), 401

def setup_hooks(app):
    @app.after_request
    def inject_x_rate_headers(response):
        limit = get_view_rate_limit()
        if limit and limit.send_x_headers:
            h = response.headers
            h.add('X-RateLimit-Remaining', str(limit.remaining))
            h.add('X-RateLimit-Limit', str(limit.limit))
            h.add('X-RateLimit-Reset', str(limit.reset))
        return response

    @app.before_request
    def api_authentication():
        """ Attempt API authentication on a per-request basis."""
        apikey = request.args.get('api_key', None)
        from flask import _request_ctx_stack
        if 'Authorization' in request.headers:
            apikey = request.headers.get('Authorization')
        if apikey:
            user = db.session.query(model.user.User).filter_by(api_key=apikey).first()
            ## HACK:
            # login_user sets a session cookie which we really don't want.
            # login_user(user)
            if user:
                _request_ctx_stack.top.user = user

    @app.context_processor
    def global_template_context():
        if current_user.is_authenticated():
            if (current_user.email_addr == current_user.name or
                    current_user.email_addr == "None"):
                flash(lazy_gettext("Please update your e-mail address in your profile page,"
                      " right now it is empty!"), 'error')

        # Cookies warning
        cookie_name = app.config['BRAND'] + "_accept_cookies"
        show_cookies_warning = False
        if not request.cookies.get(cookie_name):
            show_cookies_warning = True

        # Announcement sections
        if app.config.get('ANNOUNCEMENT'):
            announcement = app.config['ANNOUNCEMENT']
            if current_user.is_authenticated():
                for key in announcement.keys():
                    if key == 'admin' and current_user.admin:
                        flash(announcement[key], 'info')
                    if key == 'owner' and len(current_user.apps) != 0:
                        flash(announcement[key], 'info')
                    if key == 'user':
                        flash(announcement[key], 'info')

        if app.config.get('CONTACT_EMAIL'):  # pragma: no cover
            contact_email = app.config.get('CONTACT_EMAIL')
        else:
            contact_email = 'info@pybossa.com'

        if app.config.get('CONTACT_TWITTER'):  # pragma: no cover
            contact_twitter = app.config.get('CONTACT_TWITTER')
        else:
            contact_twitter = 'PyBossa'

        return dict(
            brand=app.config['BRAND'],
            title=app.config['TITLE'],
            logo=app.config['LOGO'],
            copyright=app.config['COPYRIGHT'],
            description=app.config['DESCRIPTION'],
            terms_of_use=app.config['TERMSOFUSE'],
            data_use=app.config['DATAUSE'],
            enforce_privacy=app.config['ENFORCE_PRIVACY'],
            #version=pybossa.__version__,
            current_user=current_user,
            show_cookies_warning=show_cookies_warning,
            contact_email=contact_email,
            contact_twitter=contact_twitter,
            upload_method=app.config['UPLOAD_METHOD'])
<commit_msg>Load theme fix. Closes #748<commit_after># -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2013 SF Isle of Man Limited
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.

import os
import logging
import jinja2
from flask import Flask, url_for, session, request, render_template, flash
from flask.ext.login import current_user
from flask.ext.heroku import Heroku
from flask.ext.babel import lazy_gettext

from pybossa import default_settings as settings
from pybossa.extensions import (signer, mail, login_manager, sentinel,
                                facebook, twitter, google, misaka,
                                babel, gravatar, uploader)
from pybossa.ratelimit import get_view_rate_limit

from raven.contrib.flask import Sentry
from pybossa.model import db
from pybossa import model

def create_app(theme='default'):
    template_folder = os.path.join('themes', theme, 'templates')
    static_folder = os.path.join('themes', theme, 'static')
    app = Flask(__name__, template_folder=template_folder,
                static_folder=static_folder)
    if 'DATABASE_URL' in os.environ:  # pragma: no cover
        heroku = Heroku(app)
    configure_app(app)
    setup_theme(app)
    setup_uploader(app)
    setup_error_email(app)
    setup_logging(app)
    setup_login_manager(app)
    login_manager.setup_app(app)
    setup_babel(app)
    setup_markdown(app)
    # Set up Gravatar for users
    setup_gravatar(app)
    #gravatar = Gravatar(app, size=100, rating='g', default='mm',
                        #force_default=False, force_lower=False)
    setup_db(app)
    mail.init_app(app)
    sentinel.init_app(app)
    signer.init_app(app)
    if app.config.get('SENTRY_DSN'): # pragma: no cover
        sentr = Sentry(app)
    setup_blueprints(app)
    setup_hooks(app)
    setup_error_handlers(app)
    setup_social_networks(app)
    setup_jinja(app)
    setup_geocoding(app)
    #toolbar.init_app(app)
    return app

def configure_app(app):
    app.config.from_object(settings)
    app.config.from_envvar('PYBOSSA_SETTINGS', silent=True)
    # parent directory
    if not os.environ.get('PYBOSSA_SETTINGS'): # pragma: no cover
        here = os.path.dirname(os.path.abspath(__file__))
        config_path = os.path.join(os.path.dirname(here), 'settings_local.py')
        if os.path.exists(config_path): # pragma: no cover
            app.config.from_pyfile(config_path)
    # Override DB in case of testing
    if app.config.get('SQLALCHEMY_DATABASE_TEST_URI'):
        app.config['SQLALCHEMY_DATABASE_URI'] = app.config['SQLALCHEMY_DATABASE_TEST_URI']

def setup_theme(app):
    """Configure theme for PyBossa app."""
    app.template_folder = os.path.join('themes', theme, 'templates')
    app.static_folder = os.path.join('themes', theme, 'static')

def setup_uploader(app):
    global uploader
    if app.config.get('UPLOAD_METHOD') == 'local':
        from pybossa.uploader.local import LocalUploader
        uploader = LocalUploader()
        uploader.init_app(app)
    if app.config.get('UPLOAD_METHOD') == 'rackspace':
        from pybossa.uploader.rackspace import RackspaceUploader
        uploader = RackspaceUploader()
        app.url_build_error_handlers.append(uploader.external_url_handler)
        uploader.init_app(app)

def setup_markdown(app):
    misaka.init_app(app)

def setup_db(app):
    db.app = app
    db.init_app(app)

def setup_gravatar(app):
    gravatar.init_app(app)

from logging.handlers import SMTPHandler
def setup_error_email(app):
    ADMINS = app.config.get('ADMINS', '')
    if not app.debug and ADMINS: # pragma: no cover
        mail_handler = SMTPHandler('127.0.0.1',
                                   'server-error@no-reply.com',
                                   ADMINS, 'error')
        mail_handler.setLevel(logging.ERROR)
        app.logger.addHandler(mail_handler)

from logging.handlers import RotatingFileHandler
from logging import Formatter
def setup_logging(app):
    log_file_path = app.config.get('LOG_FILE')
    log_level = app.config.get('LOG_LEVEL', logging.WARN)
    if log_file_path: # pragma: no cover
        file_handler = RotatingFileHandler(log_file_path)
        file_handler.setFormatter(Formatter(
            '%(name)s:%(levelname)s:[%(asctime)s] %(message)s '
            '[in %(pathname)s:%(lineno)d]'
            ))
        file_handler.setLevel(log_level)
        app.logger.addHandler(file_handler)
        logger = logging.getLogger('pybossa')
        logger.setLevel(log_level)
        logger.addHandler(file_handler)

def setup_login_manager(app):
    login_manager.login_view = 'account.signin'
    login_manager.login_message = u"Please sign in to access this page."
    @login_manager.user_loader
    def load_user(username):
        return db.session.query(model.user.User).filter_by(name=username).first()

# Configure theme
try: # pragma: no cover
    # First with local settings
    import settings_local
    theme = settings_local.THEME
except:
    # Otherwise try with default theme
    theme = settings.THEME

def setup_babel(app):
    """Return babel handler."""
    babel.init_app(app)

    @babel.localeselector
    def get_locale():
        if current_user.is_authenticated():
            lang = current_user.locale
        else:
            lang = session.get('lang',
                               request.accept_languages.best_match(app.config['LOCALES']))
        if lang is None:
            lang = 'en'
        return lang
    return babel
#
def setup_blueprints(app):
    """Configure blueprints."""
    from pybossa.api import blueprint as api
    from pybossa.view.account import blueprint as account
    from pybossa.view.applications import blueprint as applications
    from pybossa.view.admin import blueprint as admin
    from pybossa.view.leaderboard import blueprint as leaderboard
    from pybossa.view.stats import blueprint as stats
    from pybossa.view.help import blueprint as help
    from pybossa.view.home import blueprint as home
    from pybossa.view.uploads import blueprint as uploads

    blueprints = [{'handler': home, 'url_prefix': '/'},
                  {'handler': api,  'url_prefix': '/api'},
                  {'handler': account, 'url_prefix': '/account'},
                  {'handler': applications, 'url_prefix': '/app'},
                  {'handler': admin, 'url_prefix': '/admin'},
                  {'handler': leaderboard, 'url_prefix': '/leaderboard'},
                  {'handler': help, 'url_prefix': '/help'},
                  {'handler': stats, 'url_prefix': '/stats'},
                  {'handler': uploads, 'url_prefix': '/uploads'},
                  ]

    for bp in blueprints:
        app.register_blueprint(bp['handler'], url_prefix=bp['url_prefix'])

def setup_social_networks(app):
    try:  # pragma: no cover
        if (app.config['TWITTER_CONSUMER_KEY'] and
                app.config['TWITTER_CONSUMER_SECRET']):
            twitter.init_app(app)
            from pybossa.view.twitter import blueprint as twitter_bp
            app.register_blueprint(twitter_bp, url_prefix='/twitter')
    except Exception as inst:  # pragma: no cover
        print type(inst)
        print inst.args
        print inst
        print "Twitter signin disabled"

    # Enable Facebook if available
    try:  # pragma: no cover
        if (app.config['FACEBOOK_APP_ID'] and app.config['FACEBOOK_APP_SECRET']):
            facebook.init_app(app)
            from pybossa.view.facebook import blueprint as facebook_bp
            app.register_blueprint(facebook_bp, url_prefix='/facebook')
    except Exception as inst: # pragma: no cover
        print type(inst)
        print inst.args
        print inst
        print "Facebook signin disabled"

    # Enable Google if available
    try:  # pragma: no cover
        if (app.config['GOOGLE_CLIENT_ID'] and app.config['GOOGLE_CLIENT_SECRET']):
            google.init_app(app)
            from pybossa.view.google import blueprint as google_bp
            app.register_blueprint(google_bp, url_prefix='/google')
    except Exception as inst:  # pragma: no cover
        print type(inst)
        print inst.args
        print inst
        print "Google signin disabled"

def setup_geocoding(app):
    # Check if app stats page can generate the map
    geolite = app.root_path + '/../dat/GeoLiteCity.dat'
    if not os.path.exists(geolite):  # pragma: no cover
        app.config['GEO'] = False
        print("GeoLiteCity.dat file not found")
        print("App page stats web map disabled")
    else:  # pragma: no cover
        app.config['GEO'] = True

def url_for_other_page(page):
    args = request.view_args.copy()
    args['page'] = page
    return url_for(request.endpoint, **args)

def setup_jinja(app):
    app.jinja_env.globals['url_for_other_page'] = url_for_other_page

def setup_error_handlers(app):
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404

    @app.errorhandler(500)
    def server_error(e):  # pragma: no cover
        return render_template('500.html'), 500

    @app.errorhandler(403)
    def forbidden(e):
        return render_template('403.html'), 403

    @app.errorhandler(401)
    def unauthorized(e):
        return render_template('401.html'), 401

def setup_hooks(app):
    @app.after_request
    def inject_x_rate_headers(response):
        limit = get_view_rate_limit()
        if limit and limit.send_x_headers:
            h = response.headers
            h.add('X-RateLimit-Remaining', str(limit.remaining))
            h.add('X-RateLimit-Limit', str(limit.limit))
            h.add('X-RateLimit-Reset', str(limit.reset))
        return response

    @app.before_request
    def api_authentication():
        """ Attempt API authentication on a per-request basis."""
        apikey = request.args.get('api_key', None)
        from flask import _request_ctx_stack
        if 'Authorization' in request.headers:
            apikey = request.headers.get('Authorization')
        if apikey:
            user = db.session.query(model.user.User).filter_by(api_key=apikey).first()
            ## HACK:
            # login_user sets a session cookie which we really don't want.
            # login_user(user)
            if user:
                _request_ctx_stack.top.user = user

    @app.context_processor
    def global_template_context():
        if current_user.is_authenticated():
            if (current_user.email_addr == current_user.name or
                    current_user.email_addr == "None"):
                flash(lazy_gettext("Please update your e-mail address in your profile page,"
                      " right now it is empty!"), 'error')

        # Cookies warning
        cookie_name = app.config['BRAND'] + "_accept_cookies"
        show_cookies_warning = False
        if not request.cookies.get(cookie_name):
            show_cookies_warning = True

        # Announcement sections
        if app.config.get('ANNOUNCEMENT'):
            announcement = app.config['ANNOUNCEMENT']
            if current_user.is_authenticated():
                for key in announcement.keys():
                    if key == 'admin' and current_user.admin:
                        flash(announcement[key], 'info')
                    if key == 'owner' and len(current_user.apps) != 0:
                        flash(announcement[key], 'info')
                    if key == 'user':
                        flash(announcement[key], 'info')

        if app.config.get('CONTACT_EMAIL'):  # pragma: no cover
            contact_email = app.config.get('CONTACT_EMAIL')
        else:
            contact_email = 'info@pybossa.com'

        if app.config.get('CONTACT_TWITTER'):  # pragma: no cover
            contact_twitter = app.config.get('CONTACT_TWITTER')
        else:
            contact_twitter = 'PyBossa'

        return dict(
            brand=app.config['BRAND'],
            title=app.config['TITLE'],
            logo=app.config['LOGO'],
            copyright=app.config['COPYRIGHT'],
            description=app.config['DESCRIPTION'],
            terms_of_use=app.config['TERMSOFUSE'],
            data_use=app.config['DATAUSE'],
            enforce_privacy=app.config['ENFORCE_PRIVACY'],
            #version=pybossa.__version__,
            current_user=current_user,
            show_cookies_warning=show_cookies_warning,
            contact_email=contact_email,
            contact_twitter=contact_twitter,
            upload_method=app.config['UPLOAD_METHOD'])
<|endoftext|>


<commit_before># -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2015 SciFabric LTD.
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.
"""Module with PyBossa utils."""
from datetime import timedelta, datetime
from functools import update_wrapper
import csv
import codecs
import cStringIO
from flask import abort, request, make_response, current_app
from flask import render_template, jsonify
from functools import wraps
from flask.ext.login import current_user
from math import ceil
import json

def handle_content_type(data):
    """Return HTML or JSON based on request type."""
    if request.headers['Content-Type'] == 'application/json':
        if 'form' in data.keys():
            del data['form']
        if 'pagination' in data.keys():
            pagination = data['pagination'].to_json()
            data['pagination'] = pagination
        if 'code' in data.keys():
            return jsonify(data), data['code']
        else:
            return jsonify(data)
    else:
        template = data['template']
        del data['template']
        if 'code' in data.keys():
            error_code = data['code']
            del data['code']
            return render_template(template, **data), error_code
        else:
            return render_template(template, **data)

def jsonpify(f):
    """Wrap JSONified output for JSONP."""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        callback = request.args.get('callback', False)
        if callback:
            content = str(callback) + '(' + str(f(*args, **kwargs).data) + ')'
            return current_app.response_class(content,
                                              mimetype='application/javascript')
        else:
            return f(*args, **kwargs)
    return decorated_function

def admin_required(f):  # pragma: no cover
    """Check if the user is and admin or not."""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if current_user.admin:
            return f(*args, **kwargs)
        else:
            return abort(403)
    return decorated_function

# from http://flask.pocoo.org/snippets/56/
def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    """Crossdomain decorator."""
    if methods is not None:  # pragma: no cover
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):  # pragma: no cover
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):  # pragma: no cover
        max_age = max_age.total_seconds()

    def get_methods():  # pragma: no cover
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):

        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':  # pragma: no cover
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':  # pragma: no cover
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

# Fromhttp://stackoverflow.com/q/1551382
def pretty_date(time=False):
    """Return a pretty date.

    Get a datetime object or a int() Epoch timestamp and return a
    pretty string like 'an hour ago', 'Yesterday', '3 months ago',
    'just now', etc.
    """
    import dateutil.parser
    now = datetime.now()
    if type(time) is str or type(time) is unicode:
        time = dateutil.parser.parse(time)
    if type(time) is int:
        diff = now - datetime.fromtimestamp(time)
    if type(time) is float:
        diff = now - datetime.fromtimestamp(time)
    elif isinstance(time, datetime):
        diff = now - time
    elif not time:
        diff = now - now
    second_diff = diff.seconds
    day_diff = diff.days

    if day_diff < 0:
        return ''

    if day_diff == 0:
        if second_diff < 10:
            return "just now"
        if second_diff < 60:
            return str(second_diff) + " seconds ago"
        if second_diff < 120:
            return "a minute ago"
        if second_diff < 3600:
            return ' '.join([str(second_diff / 60), "minutes ago"])
        if second_diff < 7200:
            return "an hour ago"
        if second_diff < 86400:
            return ' '.join([str(second_diff / 3600), "hours ago"])
    if day_diff == 1:
        return "Yesterday"
    if day_diff < 7:
        return ' '.join([str(day_diff), "days ago"])
    if day_diff < 31:
        return ' '.join([str(day_diff / 7), "weeks ago"])
    if day_diff < 60:
        return ' '.join([str(day_diff / 30), "month ago"])
    if day_diff < 365:
        return ' '.join([str(day_diff / 30), "months ago"])
    if day_diff < (365 * 2):
        return ' '.join([str(day_diff / 365), "year ago"])
    return ' '.join([str(day_diff / 365), "years ago"])

class Pagination(object):

    """Class to paginate domain objects."""

    def __init__(self, page, per_page, total_count):
        """Init method."""
        self.page = page
        self.per_page = per_page
        self.total_count = total_count

    @property
    def pages(self):
        """Return number of pages."""
        return int(ceil(self.total_count / float(self.per_page)))

    @property
    def has_prev(self):
        """Return if it has a previous page."""
        return self.page > 1

    @property
    def has_next(self):
        """Return if it has a next page."""
        return self.page < self.pages

    def iter_pages(self, left_edge=0, left_current=2, right_current=3,
                   right_edge=0):
        """Iterate over pages."""
        last = 0
        for num in xrange(1, self.pages + 1):
            if (num <= left_edge or
                    (num > self.page - left_current - 1 and
                     num < self.page + right_current) or
                    num > self.pages - right_edge):
                if last + 1 != num:  # pragma: no cover
                    yield None
                yield num
                last = num

    def to_json(self):
        """Return the object in JSON format."""
        return dict(page=self.page,
                    per_page=self.per_page,
                    total=self.total_count,
                    next=self.has_next,
                    prev=self.has_prev)

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    """Unicode CSV reader."""
    # This code is taken from http://docs.python.org/library/csv.html#examples
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    """UTF8 encoder for CSV data."""
    # This code is taken from http://docs.python.org/library/csv.html#examples
    for line in unicode_csv_data:
        yield line.encode('utf-8')

class UnicodeWriter:

    """A CSV writer which will write rows to CSV file "f"."""

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        """Init method."""
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        """Write row."""
        line = []
        for s in row:
            if (type(s) == dict):
                line.append(json.dumps(s))
            else:
                line.append(unicode(s).encode("utf-8"))
        self.writer.writerow(line)
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):  # pragma: no cover
        """Write rows."""
        for row in rows:
            self.writerow(row)

def get_user_signup_method(user):
    """Return which OAuth sign up method the user used."""
    msg = u'Sorry, there is already an account with the same e-mail.'
    if user.info:
        # Google
        if user.info.get('google_token'):
            msg += " It seems like you signed up with your Google account."
            msg += "
You can try and sign in by clicking in the Google button."
            return (msg, 'google')
        # Facebook
        elif user.info.get('facebook_token'):
            msg += " It seems like you signed up with your Facebook account."
            msg += "
You can try and sign in by clicking in the Facebook button."
            return (msg, 'facebook')
        # Twitter
        elif user.info.get('twitter_token'):
            msg += " It seems like you signed up with your Twitter account."
            msg += "
You can try and sign in by clicking in the Twitter button."
            return (msg, 'twitter')
        # Local account
        else:
            msg += " It seems that you created an account locally."
            msg += "
You can reset your password if you don't remember it."
            return (msg, 'local')
    else:
        msg += " It seems that you created an account locally."
        msg += "
You can reset your password if you don't remember it."
        return (msg, 'local')

def get_port():
    """Get port."""
    import os
    port = os.environ.get('PORT', '')
    if port.isdigit():
        return int(port)
    else:
        return current_app.config['PORT']

def get_user_id_or_ip():
    """Return the id of the current user if is authenticated.
    Otherwise returns its IP address (defaults to 127.0.0.1).
    """
    user_id = current_user.id if current_user.is_authenticated() else None
    user_ip = request.remote_addr or "127.0.0.1" \
        if current_user.is_anonymous() else None
    return dict(user_id=user_id, user_ip=user_ip)

def with_cache_disabled(f):
    """Decorator that disables the cache for the execution of a function.
    It enables it back when the function call is done.
    """
    import os

    @wraps(f)
    def wrapper(*args, **kwargs):
        env_cache_disabled = os.environ.get('PYBOSSA_REDIS_CACHE_DISABLED')
        if env_cache_disabled is None or env_cache_disabled is '0':
            os.environ['PYBOSSA_REDIS_CACHE_DISABLED'] = '1'
        return_value = f(*args, **kwargs)
        if env_cache_disabled is None:
            del os.environ['PYBOSSA_REDIS_CACHE_DISABLED']
        else:
            os.environ['PYBOSSA_REDIS_CACHE_DISABLED'] = env_cache_disabled
        return return_value
    return wrapper

def is_reserved_name(blueprint, name):
    """Check if a name has already been registered inside a blueprint URL."""
    path = ''.join(['/', blueprint])
    app_urls = [r.rule for r in current_app.url_map.iter_rules()
                if r.rule.startswith(path)]
    reserved_names = [url.split('/')[2] for url in app_urls
                      if url.split('/')[2] != '']
    return name in reserved_names

def username_from_full_name(username):
    """Takes a username that may contain several words with capital letters and
    returns a single word username, no spaces, all lowercases."""
    if type(username) == str:
        return username.decode('ascii', 'ignore').lower().replace(' ', '')
    return username.encode('ascii', 'ignore').decode('utf-8').lower().replace(' ', '')

def rank(projects):
    """Takes a list of (published) projects (as dicts) and orders them by
    activity, number of volunteers, number of tasks and other criteria."""
    def earned_points(project):
        points = 0
        if project['overall_progress'] != 100L:
            points += 1000
        if not ('test' in project['name'].lower()
                or 'test' in project['short_name'].lower()):
            points += 500
        if project['info'].get('thumbnail'):
            points += 200
        points += _points_by_interval(project['n_tasks'], weight=1)
        points += _points_by_interval(project['n_volunteers'], weight=2)
        points += _last_activity_points(project)
        return points

    projects.sort(key=earned_points, reverse=True)
    return projects

def _last_activity_points(project):
    default = datetime(1970, 1, 1, 0, 0).strftime('%Y-%m-%dT%H:%M:%S')
    updated_datetime = (project.get('updated') or default)
    last_activity_datetime = (project.get('last_activity_raw') or default)
    updated_datetime = updated_datetime.split('.')[0]
    last_activity_datetime = last_activity_datetime.split('.')[0]
    updated = datetime.strptime(updated_datetime, '%Y-%m-%dT%H:%M:%S')
    last_activity = datetime.strptime(last_activity_datetime, '%Y-%m-%dT%H:%M:%S')
    most_recent = max(updated, last_activity)

    days_since_modified = (datetime.utcnow() - most_recent).days

    if days_since_modified < 1:
        return 50
    if days_since_modified < 2:
        return 20
    if days_since_modified < 3:
        return 10
    if days_since_modified < 4:
        return 5
    return 0

def _points_by_interval(value, weight=1):
    if value > 100:
        return 20 * weight
    if value > 50:
        return 15 * weight
    if value > 20:
        return 10 * weight
    if value > 10:
        return 5 * weight
    if value > 0:
        return 1 * weight
    return 0

def publish_channel(sentinel, project_short_name, data, type, private=True):
    """Publish in a channel some JSON data as a string."""
    if private:
        channel = "channel_%s_%s" % ("private", project_short_name)
    else:
        channel = "channel_%s_%s" % ("public", project_short_name)
    msg = dict(type=type, data=data)
    sentinel.master.publish(channel, json.dumps(msg))
<commit_msg>Handle form errors and CSRF token.<commit_after># -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2015 SciFabric LTD.
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.
"""Module with PyBossa utils."""
from datetime import timedelta, datetime
from functools import update_wrapper
import csv
import codecs
import cStringIO
from flask import abort, request, make_response, current_app
from flask import render_template, jsonify
from flask_wtf.csrf import generate_csrf
from functools import wraps
from flask.ext.login import current_user
from math import ceil
import json

def handle_content_type(data):
    """Return HTML or JSON based on request type."""
    if request.headers['Content-Type'] == 'application/json':
        if 'form' in data.keys():
            tmp = data['form']
            data['form'] = tmp.data
            data['form']['csrf'] = generate_csrf()
            data['form']['errors'] = tmp.errors
        if 'pagination' in data.keys():
            pagination = data['pagination'].to_json()
            data['pagination'] = pagination
        if 'code' in data.keys():
            return jsonify(data), data['code']
        else:
            return jsonify(data)
    else:
        template = data['template']
        del data['template']
        if 'code' in data.keys():
            error_code = data['code']
            del data['code']
            return render_template(template, **data), error_code
        else:
            return render_template(template, **data)

def jsonpify(f):
    """Wrap JSONified output for JSONP."""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        callback = request.args.get('callback', False)
        if callback:
            content = str(callback) + '(' + str(f(*args, **kwargs).data) + ')'
            return current_app.response_class(content,
                                              mimetype='application/javascript')
        else:
            return f(*args, **kwargs)
    return decorated_function

def admin_required(f):  # pragma: no cover
    """Check if the user is and admin or not."""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if current_user.admin:
            return f(*args, **kwargs)
        else:
            return abort(403)
    return decorated_function

# from http://flask.pocoo.org/snippets/56/
def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    """Crossdomain decorator."""
    if methods is not None:  # pragma: no cover
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):  # pragma: no cover
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):  # pragma: no cover
        max_age = max_age.total_seconds()

    def get_methods():  # pragma: no cover
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):

        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':  # pragma: no cover
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':  # pragma: no cover
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

# Fromhttp://stackoverflow.com/q/1551382
def pretty_date(time=False):
    """Return a pretty date.

    Get a datetime object or a int() Epoch timestamp and return a
    pretty string like 'an hour ago', 'Yesterday', '3 months ago',
    'just now', etc.
    """
    import dateutil.parser
    now = datetime.now()
    if type(time) is str or type(time) is unicode:
        time = dateutil.parser.parse(time)
    if type(time) is int:
        diff = now - datetime.fromtimestamp(time)
    if type(time) is float:
        diff = now - datetime.fromtimestamp(time)
    elif isinstance(time, datetime):
        diff = now - time
    elif not time:
        diff = now - now
    second_diff = diff.seconds
    day_diff = diff.days

    if day_diff < 0:
        return ''

    if day_diff == 0:
        if second_diff < 10:
            return "just now"
        if second_diff < 60:
            return str(second_diff) + " seconds ago"
        if second_diff < 120:
            return "a minute ago"
        if second_diff < 3600:
            return ' '.join([str(second_diff / 60), "minutes ago"])
        if second_diff < 7200:
            return "an hour ago"
        if second_diff < 86400:
            return ' '.join([str(second_diff / 3600), "hours ago"])
    if day_diff == 1:
        return "Yesterday"
    if day_diff < 7:
        return ' '.join([str(day_diff), "days ago"])
    if day_diff < 31:
        return ' '.join([str(day_diff / 7), "weeks ago"])
    if day_diff < 60:
        return ' '.join([str(day_diff / 30), "month ago"])
    if day_diff < 365:
        return ' '.join([str(day_diff / 30), "months ago"])
    if day_diff < (365 * 2):
        return ' '.join([str(day_diff / 365), "year ago"])
    return ' '.join([str(day_diff / 365), "years ago"])

class Pagination(object):

    """Class to paginate domain objects."""

    def __init__(self, page, per_page, total_count):
        """Init method."""
        self.page = page
        self.per_page = per_page
        self.total_count = total_count

    @property
    def pages(self):
        """Return number of pages."""
        return int(ceil(self.total_count / float(self.per_page)))

    @property
    def has_prev(self):
        """Return if it has a previous page."""
        return self.page > 1

    @property
    def has_next(self):
        """Return if it has a next page."""
        return self.page < self.pages

    def iter_pages(self, left_edge=0, left_current=2, right_current=3,
                   right_edge=0):
        """Iterate over pages."""
        last = 0
        for num in xrange(1, self.pages + 1):
            if (num <= left_edge or
                    (num > self.page - left_current - 1 and
                     num < self.page + right_current) or
                    num > self.pages - right_edge):
                if last + 1 != num:  # pragma: no cover
                    yield None
                yield num
                last = num

    def to_json(self):
        """Return the object in JSON format."""
        return dict(page=self.page,
                    per_page=self.per_page,
                    total=self.total_count,
                    next=self.has_next,
                    prev=self.has_prev)

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    """Unicode CSV reader."""
    # This code is taken from http://docs.python.org/library/csv.html#examples
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    """UTF8 encoder for CSV data."""
    # This code is taken from http://docs.python.org/library/csv.html#examples
    for line in unicode_csv_data:
        yield line.encode('utf-8')

class UnicodeWriter:

    """A CSV writer which will write rows to CSV file "f"."""

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        """Init method."""
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        """Write row."""
        line = []
        for s in row:
            if (type(s) == dict):
                line.append(json.dumps(s))
            else:
                line.append(unicode(s).encode("utf-8"))
        self.writer.writerow(line)
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):  # pragma: no cover
        """Write rows."""
        for row in rows:
            self.writerow(row)

def get_user_signup_method(user):
    """Return which OAuth sign up method the user used."""
    msg = u'Sorry, there is already an account with the same e-mail.'
    if user.info:
        # Google
        if user.info.get('google_token'):
            msg += " It seems like you signed up with your Google account."
            msg += "
You can try and sign in by clicking in the Google button."
            return (msg, 'google')
        # Facebook
        elif user.info.get('facebook_token'):
            msg += " It seems like you signed up with your Facebook account."
            msg += "
You can try and sign in by clicking in the Facebook button."
            return (msg, 'facebook')
        # Twitter
        elif user.info.get('twitter_token'):
            msg += " It seems like you signed up with your Twitter account."
            msg += "
You can try and sign in by clicking in the Twitter button."
            return (msg, 'twitter')
        # Local account
        else:
            msg += " It seems that you created an account locally."
            msg += "
You can reset your password if you don't remember it."
            return (msg, 'local')
    else:
        msg += " It seems that you created an account locally."
        msg += "
You can reset your password if you don't remember it."
        return (msg, 'local')

def get_port():
    """Get port."""
    import os
    port = os.environ.get('PORT', '')
    if port.isdigit():
        return int(port)
    else:
        return current_app.config['PORT']

def get_user_id_or_ip():
    """Return the id of the current user if is authenticated.
    Otherwise returns its IP address (defaults to 127.0.0.1).
    """
    user_id = current_user.id if current_user.is_authenticated() else None
    user_ip = request.remote_addr or "127.0.0.1" \
        if current_user.is_anonymous() else None
    return dict(user_id=user_id, user_ip=user_ip)

def with_cache_disabled(f):
    """Decorator that disables the cache for the execution of a function.
    It enables it back when the function call is done.
    """
    import os

    @wraps(f)
    def wrapper(*args, **kwargs):
        env_cache_disabled = os.environ.get('PYBOSSA_REDIS_CACHE_DISABLED')
        if env_cache_disabled is None or env_cache_disabled is '0':
            os.environ['PYBOSSA_REDIS_CACHE_DISABLED'] = '1'
        return_value = f(*args, **kwargs)
        if env_cache_disabled is None:
            del os.environ['PYBOSSA_REDIS_CACHE_DISABLED']
        else:
            os.environ['PYBOSSA_REDIS_CACHE_DISABLED'] = env_cache_disabled
        return return_value
    return wrapper

def is_reserved_name(blueprint, name):
    """Check if a name has already been registered inside a blueprint URL."""
    path = ''.join(['/', blueprint])
    app_urls = [r.rule for r in current_app.url_map.iter_rules()
                if r.rule.startswith(path)]
    reserved_names = [url.split('/')[2] for url in app_urls
                      if url.split('/')[2] != '']
    return name in reserved_names

def username_from_full_name(username):
    """Takes a username that may contain several words with capital letters and
    returns a single word username, no spaces, all lowercases."""
    if type(username) == str:
        return username.decode('ascii', 'ignore').lower().replace(' ', '')
    return username.encode('ascii', 'ignore').decode('utf-8').lower().replace(' ', '')

def rank(projects):
    """Takes a list of (published) projects (as dicts) and orders them by
    activity, number of volunteers, number of tasks and other criteria."""
    def earned_points(project):
        points = 0
        if project['overall_progress'] != 100L:
            points += 1000
        if not ('test' in project['name'].lower()
                or 'test' in project['short_name'].lower()):
            points += 500
        if project['info'].get('thumbnail'):
            points += 200
        points += _points_by_interval(project['n_tasks'], weight=1)
        points += _points_by_interval(project['n_volunteers'], weight=2)
        points += _last_activity_points(project)
        return points

    projects.sort(key=earned_points, reverse=True)
    return projects

def _last_activity_points(project):
    default = datetime(1970, 1, 1, 0, 0).strftime('%Y-%m-%dT%H:%M:%S')
    updated_datetime = (project.get('updated') or default)
    last_activity_datetime = (project.get('last_activity_raw') or default)
    updated_datetime = updated_datetime.split('.')[0]
    last_activity_datetime = last_activity_datetime.split('.')[0]
    updated = datetime.strptime(updated_datetime, '%Y-%m-%dT%H:%M:%S')
    last_activity = datetime.strptime(last_activity_datetime, '%Y-%m-%dT%H:%M:%S')
    most_recent = max(updated, last_activity)

    days_since_modified = (datetime.utcnow() - most_recent).days

    if days_since_modified < 1:
        return 50
    if days_since_modified < 2:
        return 20
    if days_since_modified < 3:
        return 10
    if days_since_modified < 4:
        return 5
    return 0

def _points_by_interval(value, weight=1):
    if value > 100:
        return 20 * weight
    if value > 50:
        return 15 * weight
    if value > 20:
        return 10 * weight
    if value > 10:
        return 5 * weight
    if value > 0:
        return 1 * weight
    return 0

def publish_channel(sentinel, project_short_name, data, type, private=True):
    """Publish in a channel some JSON data as a string."""
    if private:
        channel = "channel_%s_%s" % ("private", project_short_name)
    else:
        channel = "channel_%s_%s" % ("public", project_short_name)
    msg = dict(type=type, data=data)
    sentinel.master.publish(channel, json.dumps(msg))
<|endoftext|>


<commit_before>from pyfancy import *

pyfancy().red("Hello").raw(", ").blue("world!").output()
pyfancy().multi("Multicolored text").output()
pyfancy().rainbow("Rainbow text").output()
pyfancy().parse("{red foo {bold bar} baz}").output()
pyfancy().black_bg("Black background").output()
pyfancy().green_bg("Green background").output()
pyfancy().red_bg("Red background").output()
pyfancy().black().yellow_bg("Yellow background").output()
pyfancy().blue_bg("Blue background").output()
pyfancy().purple_bg("Purple background").output()
pyfancy().cyan_bg("Cyan background").output()
pyfancy().black().gray_bg("Gray background").output()
pyfancy().read("/demo/import.txt").output()
print(pyfancy().red("Hello").raw(", ").blue("world!").strip())
<commit_msg>another attempt<commit_after>from pyfancy import *

pyfancy().red("Hello").raw(", ").blue("world!").output()
pyfancy().multi("Multicolored text").output()
pyfancy().rainbow("Rainbow text").output()
pyfancy().parse("{red foo {bold bar} baz}").output()
pyfancy().black_bg("Black background").output()
pyfancy().green_bg("Green background").output()
pyfancy().red_bg("Red background").output()
pyfancy().black().yellow_bg("Yellow background").output()
pyfancy().blue_bg("Blue background").output()
pyfancy().purple_bg("Purple background").output()
pyfancy().cyan_bg("Cyan background").output()
pyfancy().black().gray_bg("Gray background").output()
pyfancy().read("pyfancy/demo/import.txt").output()
print(pyfancy().red("Hello").raw(", ").blue("world!").strip())
<|endoftext|>