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})
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})
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()
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")
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)
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})
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"))
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)
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()
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
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
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
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})
# 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,
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"))
class Meta: label = 'base' description = _("Sandglass command line interface") config_section = 'cli:main'
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.
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