Beispiel #1
0
    def put_collection(self, session):
        """
        Update one or multiple objects.

        Each object MUST contain its original pk value.

        """
        data_list = self.submitted_collection_data
        # Update all members in data list
        count = 0
        query = session.query(self.model)
        try:
            for data in data_list:
                pk_value = data.pop('id')
                update_query = query.filter(self.model.id == pk_value)
                update_query.update(data)
                count += 1
        except:
            LOG.exception('Error updating object(s) during PUT request')
            transaction.doom()
            return error_response(_("Object(s) update failed"))

        if not count:
            return error_response(_("No object(s) updated"))

        msg = _("Object(s) updated successfully")
        # TODO: Check support for rowcount
        # http://docs.sqlalchemy.org/en/latest/core/connections.html
        # sqlalchemy.engine.ResultProxy.rowcount
        return info_response(msg, data={'count': count})
Beispiel #2
0
    def delete_related(self):
        """
        Remove related objects from current object.

        Removal is only allowed for related objects that are collections.

        When an object or list of objects is submitted, the only needed
        mandatory field is the `id` field; All other fields are ignored.

        A request could contain a list of objects:

            [{'id': 1, ..}, {'id': 99, ..}, ..]

        or a list of integers:

            [1, 99, ..]

        """
        related = getattr(self.object, self.related_name, None)
        if not related:
            return info_response(_("Nothing to delete"), data={'count': 0})
        elif not isinstance(related, list):
            raise APIError('OBJECT_NOT_ALLOWED')
        else:
            # Related is a collection of objects
            related_list = related
            remove_id_list = self._get_submitted_related_id_list()
            count = 0
            for obj in related_list[:]:
                if obj.id in remove_id_list:
                    related_list.remove(obj)
                    count += 1

            msg = _("Object(s) deleted successfully")
            return info_response(msg, data={'count': count})
Beispiel #3
0
def database_insert_default_data(session=None, commit=True):
    """
    Insert initial database data.

    This must be called only once to setup initial database recods.

    """
    if not session:
        session = DBSESSION()

    manager = PermissionManager(session)

    # Add default groups
    session.add_all([
        Group(
            name=security.Administrators,
            description=_(u"Administrators"),
        ),
        Group(
            name=security.Users,
            description=_(u"Users"),
            permissions=manager.users_group_permissions,
        ),
        Group(
            name=security.Managers,
            description=_(u"Managers"),
            permissions=manager.managers_group_permissions,
        )
    ])

    if commit:
        transaction.commit()
Beispiel #4
0
    def validate(values):
        # Add a limit for the number of values
        max_values = 100
        if len(values) > max_values:
            msg = _(u"A maximum of {} IDs are allowed per request")
            return msg.format(max_values)

        # Check each value in list
        for value in values:
            try:
                int(value)
            except (ValueError, TypeError):
                return _(u"Invalid ID value")
Beispiel #5
0
def post_argument_parsing_hook(app):
    """
    Called after application arguments are parsed.

    """
    # Load application config
    config_file = app.pargs.config
    config_loaded = app.config.parse_file(config_file)
    if not config_loaded:
        msg = _("Config file {} could not be loaded").format(config_file)
        app.log.error(msg)
        sys.exit(1)
    else:
        msg = _("Using config file {}").format(config_file)
        app.log.debug(msg)
Beispiel #6
0
    def delete_collection(self):
        """
        Delete all model objects.

        """
        id_list = [data['id'] for data in self.submitted_collection_data]
        query = self.model.query().filter(self.model.id.in_(id_list))
        count = query.delete(False)

        if not count:
            msg = _("No objects were deleted")
        else:
            msg = _("Object(s) deleted successfully")

        return info_response(msg, data={'count': count})
Beispiel #7
0
    def init_database(self):
        """
        Insert initial database records to a newly created database.

        """
        install.database_insert_default_data()
        print(_("Sandglass is ready to run now"))
Beispiel #8
0
class RequirePermissions(object):
    """
    SchemaNode validator to check user permissions during serialization.

    Validator check that current logged in user has one or more permissions
    for a given schema field.

    """
    invalid_msg = _("No permission to change {field_name} value")

    def __init__(self, *permissions):
        self.permissions = set(permissions)

    def __call__(self, node, value):
        request = node.bindings.get('request')
        if not request:
            raise Exception("Validator is not bound to a request")

        error_message = self.invalid_msg.format(field_name=node.name)
        user = request.authenticated_user
        if user:
            # Admin users are allowed to change any field
            if user.is_admin:
                return

            # Check that user user has right permissions for this field
            if self.permissions.issubset(user.permissions):
                return

        raise Invalid(node, error_message)
Beispiel #9
0
    def activities(self):
        """
        Get activities for current user.

        By default activities are getted only for current day.
        Different date range can be queried using `from` and `to`
        arguments in the request.

        Returns a List of Activity.

        """
        if not self.is_valid_object:
            raise NotFound()

        try:
            (from_date, to_date) = self.get_filter_from_to()
        except (TypeError, ValueError):
            return error_response(_('Invalid date format'))

        # When no dates are given use current date as default
        if not from_date and not to_date:
            from_date = datetime.date.today()
            to_date = from_date + datetime.timedelta(days=1)

        query = Activity.query()
        query = query.filter(Activity.user_id == self.pk_value)
        if from_date:
            query = query.filter(Activity.start >= from_date)
        if to_date:
            query = query.filter(Activity.start < to_date)

        return query.all()
Beispiel #10
0
    def put_member(self):
        """
        Update current object data.

        """
        query = self.object.query()
        try:
            count = query.update(self.submitted_member_data)
        except colander.Invalid:
            raise
        except:
            LOG.exception('Error updating object during PUT request')
            transaction.doom()
            return error_response(_("Object update failed"))

        if not count:
            return error_response(_("No object was updated"))

        return self.object
Beispiel #11
0
    def delete_member(self):
        """
        Delete current object from database.

        """
        serialized_object = dict(self.object)
        query = self.object.query()
        count = query.delete()
        if not count:
            return error_response(_('No object was deleted'))
        else:
            # Return the deleted object
            return serialized_object
Beispiel #12
0
    def post_collection(self, session):
        """
        Create new object(s).

        Request body can be a JSON object or a list of objects.

        """
        obj_list = []
        for data in self.submitted_collection_data:
            obj = self.model(**data)
            session.add(obj)
            obj_list.append(obj)

        # Flush to generate IDs
        try:
            session.flush()
        except:
            msg = "Unable to flush POST collectiond data for /%s"
            LOG.exception(msg, self.get_route_prefix())
            return error_response(_("Error creating object(s)"))

        return obj_list
Beispiel #13
0
    def put_related(self):
        """
        Update related object collection.

        Update is only allowed for related objects that are collections.

        When an object or list of objects is submitted, the only needed
        mandatory field is the `id` field; All other fields are ignored.

        A request could contain a list of objects:

            [{'id': 1, ..}, {'id': 99, ..}, ..]

        or a list of integers:

            [1, 99, ..]

        """
        related = getattr(self.object, self.related_name)
        relationship = self.relationships[self.related_name]
        # Check that relationship is using a list and not a single object
        if not relationship.uselist:
            raise APIError('OBJECT_NOT_ALLOWED')

        # Create a query to get related objects to be appended
        update_id_list = self._get_submitted_related_id_list()
        related_class = relationship.mapper.class_
        query = related_class.query(session=self.object.current_session)
        query = query.filter(related_class.id.in_(update_id_list))
        # Add related objects to current member
        count = 0
        for obj in query.all():
            related.append(obj)
            count += 1

        msg = _("Object(s) added successfully")
        return info_response(msg, data={'count': count})
Beispiel #14
0
# TODO: Use Enum instead of globals
# Activity codes
ACTIVITY_UNASSIGNED = 'unassigned'
ACTIVITY_WORKING = 'working'
ACTIVITY_BREAK = 'break'
ACTIVITY_TRIP = 'trip'
INACTIVITY_VACATION = 'vacation'
INACTIVITY_HOLIDAY = 'holiday'
INACTIVITY_SICK = 'sick'
INACTIVITY_ONLEAVE = 'onleave'
INACTIVITY_APPOINTMENT = 'appointment'

# Supported types of tags
ACTIVITY_TYPES = {
    ACTIVITY_UNASSIGNED: _(u"unassigned"),
    ACTIVITY_WORKING: _(u"working"),
    ACTIVITY_BREAK: _(u"on break"),
    ACTIVITY_TRIP: _(u"business trip"),
    INACTIVITY_VACATION: _(u"on vacation"),
    INACTIVITY_HOLIDAY: _(u"public holiday"),
    INACTIVITY_SICK: _(u"sick"),
    INACTIVITY_ONLEAVE: _(u"on leave"),
    INACTIVITY_APPOINTMENT: _(u"official appointment"),
}


# Table definition to store the tags used in and activity
tag_association_table = Table(
    'time_activity_tag',
    META,
Beispiel #15
0
    def create_user(self):
        """
        Create a new user.

        """
        print(_("Create a new user"), '\n')
        email = self.app.input(_("E-Mail"))
        if not is_valid_email(email):
            self.app.log.error(_("E-Mail address is not valid"))
            sys.exit(1)

        if User.query().filter_by(email=email).count():
            self.app.log.error(_("A user with same E-Mail already exists"))
            sys.exit(1)

        data = {}
        data['email'] = email
        data['first_name'] = self.app.input(_("First name"))
        data['last_name'] = self.app.input(_("Last name"))
        if not (data['first_name'] and data['last_name']):
            self.app.log.error(_("User first and last names are mandatory"))

        # Get password for new user
        password = self.app.input(_("Password"), echo=False)
        while self.app.input(_("Repeat password"), echo=False) != password:
            print(_("Passwords don't match"))
            password = self.app.input(_("Password"), echo=False)

        if not (password or '').strip():
            print(_("No password entered. User is not created."))
            sys.exit(1)

        data['password'] = password

        user = User(**data)
        session = user.new_session()
        session.add(user)

        # Get the group name for the user
        group_name = None
        if self.app.pargs.admin:
            # When user is an admin add it to admins group
            group_name = Administrators
        elif self.app.pargs.group:
            group_name = self.app.pargs.group.decode(sys.stdin.encoding)

        if group_name:
            group = Group.query().filter_by(name=group_name).first()
            if not group:
                print(_("Group {} is not defined").format(group_name))
                transaction.doom()
                sys.exit(1)

            user.groups.append(group)

        # Print token and key after creation
        msg = _("Token: {0}\nKey: {1}").format(user.token, user.key)
        print(msg, '\n')

        transaction.commit()
        print('\n', _("User created successfully"))
Beispiel #16
0
 class Meta:
     label = 'base'
     description = _("Sandglass command line interface")
     config_section = 'cli:main'
Beispiel #17
0
from colander import SchemaNode
from pyramid.decorator import reify
from pyramid.path import DottedNameResolver
from zope import interface

from sandglass.time import _
from sandglass.time.describe.interfaces import IDescribable
from sandglass.time.filters import NULL
from sandglass.time.filters import QueryFilter
from sandglass.time.filters import QueryFilterError

LOG = logging.getLogger(__name__)

# Global error/warning messages
LOG_MESSAGES = {
    'value_is_not_string': _("Filter value %s is not a string"),
    'value_is_not_list': _("Filter value %s is not a list"),
}


def _apply_operation_eq(query, field, value):
    """
    Apply "is equal" operation to a query field.

    """
    return query.filter(field == value)


def _apply_operation_neq(query, field, value):
    """
    Apply "is not equal" operation to a query field.
Beispiel #18
0
from sandglass.time import _
from sandglass.time.api.error import APIError


# API error codes and messages
CODES = {
    'INVALID_SIGNIN': _("Invalid sign in credentials"),
    'USER_EMAIL_EXISTS': _("A user with the same E-Mail already exists"),
    'USER_NOT_FOUND': _("User not found"),
}


class APIV1Error(APIError):
    """
    Exception class for API v1 errors.

    """
    # Dictionary with the error codes to use in this type of exceptions
    codes = CODES