Пример #1
0
    class DbNote(db.Model, NoteBase):
        _logger = logging_util.get_logger_by_name(__name__, 'DbNote')

        __tablename__ = 'note'

        id = db.Column(db.Integer, primary_key=True)
        content = db.Column(db.String(4000))
        timestamp = db.Column(db.DateTime)

        task_id = db.Column(db.Integer, db.ForeignKey('task.id'))
        task = db.relationship('DbTask',
                               backref=db.backref('notes',
                                                  lazy='dynamic',
                                                  order_by=timestamp))

        def __init__(self, content, timestamp=None, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            db.Model.__init__(self)
            NoteBase.__init__(self, content, timestamp)

        @classmethod
        def from_dict(cls, d, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            return super(DbNote, cls).from_dict(d=d, lazy=None)

        def clear_relationships(self):
            self.task = None
Пример #2
0
    class DbUser(db.Model, UserBase):
        _logger = logging_util.get_logger_by_name(__name__, 'DbUser')

        __tablename__ = 'user'

        id = db.Column(db.Integer, primary_key=True)
        email = db.Column(db.String(100), nullable=False, unique=True)
        hashed_password = db.Column(db.String(100))
        is_admin = db.Column(db.Boolean, nullable=False, default=False)

        tasks = db.relationship('DbTask',
                                secondary=users_tasks_table,
                                back_populates='users')

        def __init__(self,
                     email,
                     hashed_password=None,
                     is_admin=False,
                     lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            db.Model.__init__(self)
            UserBase.__init__(self,
                              email=email,
                              hashed_password=hashed_password,
                              is_admin=is_admin)

        @classmethod
        def from_dict(cls, d, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            return super(DbUser, cls).from_dict(d=d, lazy=None)

        def clear_relationships(self):
            self.tasks = []
Пример #3
0
class Option(Changeable, OptionBase):
    _logger = logging_util.get_logger_by_name(__name__, 'Option')

    _key = None
    _value = None

    @property
    def key(self):
        return self._key

    @key.setter
    def key(self, value):
        if value != self._key:
            self._on_attr_changing(self.FIELD_KEY, self._key)
            self._key = value
            self._on_attr_changed(self.FIELD_KEY, self.OP_SET, self._key)

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

    @value.setter
    def value(self, value):
        if value != self._value:
            self._on_attr_changing(self.FIELD_VALUE,
                                   self._value)
            self._value = value
            self._on_attr_changed(self.FIELD_VALUE, self.OP_SET, self._value)

    def clear_relationships(self):
        pass
Пример #4
0
Файл: tag.py Проект: izrik/tudor
    class DbTag(db.Model, TagBase):
        _logger = logging_util.get_logger_by_name(__name__, 'DbTag')

        __tablename__ = 'tag'

        id = db.Column(db.Integer, primary_key=True)
        value = db.Column(db.String(100), nullable=False, unique=True)
        description = db.Column(db.String(4000), nullable=True)

        tasks = db.relationship('DbTask',
                                secondary=tags_tasks_table,
                                back_populates='tags')

        def __init__(self, value, description=None, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            db.Model.__init__(self)
            TagBase.__init__(self, value, description)

        @classmethod
        def from_dict(cls, d, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            return super(DbTag, cls).from_dict(d=d, lazy=None)

        def clear_relationships(self):
            self.tasks = []
Пример #5
0
    class DbOption(db.Model, OptionBase):
        _logger = logging_util.get_logger_by_name(__name__, 'DbOption')

        __tablename__ = 'option'

        key = db.Column(db.String(100), primary_key=True)
        value = db.Column(db.String(100), nullable=True)

        def __init__(self, key, value, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            db.Model.__init__(self)
            OptionBase.__init__(self, key, value)

        @classmethod
        def from_dict(cls, d, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            return super(DbOption, cls).from_dict(d=d, lazy=None)

        def clear_relationships(self):
            pass
Пример #6
0
    class DbAttachment(db.Model, AttachmentBase):
        _logger = logging_util.get_logger_by_name(__name__, 'DbAttachment')

        __tablename__ = 'attachment'

        id = db.Column(db.Integer, primary_key=True)
        path = db.Column(db.String(1000), nullable=False)
        timestamp = db.Column(db.DateTime)
        filename = db.Column(db.String(100))
        description = db.Column(db.String(100), default=None)

        task_id = db.Column(db.Integer, db.ForeignKey('task.id'))
        task = db.relationship('DbTask',
                               backref=db.backref('attachments',
                                                  lazy='dynamic',
                                                  order_by=timestamp))

        def __init__(self,
                     path,
                     description=None,
                     timestamp=None,
                     filename=None,
                     lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            db.Model.__init__(self)
            AttachmentBase.__init__(self, path, description, timestamp,
                                    filename)

        @classmethod
        def from_dict(cls, d, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            return super(DbAttachment, cls).from_dict(d=d, lazy=None)

        def clear_relationships(self):
            self.task = None
Пример #7
0
class OneToManySet(InterlinkedSet):
    _logger = logging_util.get_logger_by_name(__name__, 'OneToManySet')

    def add(self, item):
        self._logger.debug('add')
        self._logger.debug('%s: %s', self.c, item)
        if item not in self:
            self._logger.debug('adding the item')
            self.container._on_attr_changing(self.__change_field__, None)
            self._add(item)
            setattr(item, self.__attr_counterpart__, self.container)
            self.container._on_attr_changed(self.__change_field__,
                                            Changeable.OP_ADD, item)

    def discard(self, item):
        self._logger.debug('discard')
        self._logger.debug('%s: %s', self.c, item)
        if item in self:
            self._logger.debug('discarding the item')
            self.container._on_attr_changing(self.__change_field__, None)
            self._discard(item)
            setattr(item, self.__attr_counterpart__, None)
            self.container._on_attr_changed(self.__change_field__,
                                            Changeable.OP_REMOVE, item)
Пример #8
0
    class DbTask(db.Model, TaskBase):
        _logger = logging_util.get_logger_by_name(__name__, 'DbTask')

        __tablename__ = 'task'

        id = db.Column(db.Integer, primary_key=True)
        summary = db.Column(db.String(100))
        description = db.Column(db.String(4000))
        is_done = db.Column(db.Boolean)
        is_deleted = db.Column(db.Boolean)
        order_num = db.Column(db.Integer, nullable=False, default=0)
        deadline = db.Column(db.DateTime)
        expected_duration_minutes = db.Column(db.Integer)
        expected_cost = db.Column(db.Numeric)
        is_public = db.Column(db.Boolean)
        tags = db.relationship('DbTag', secondary=tags_tasks_table,
                               back_populates="tasks")

        users = db.relationship('DbUser', secondary=users_tasks_table,
                                back_populates="tasks")

        parent_id = db.Column(db.Integer, db.ForeignKey('task.id'),
                              nullable=True)
        parent = db.relationship('DbTask', remote_side=[id],
                                 backref=db.backref('children',
                                                    lazy='dynamic'))

        # self depends on self.dependees
        # self.dependants depend on self
        dependees = db.relationship(
            'DbTask', secondary=task_dependencies_table,
            primaryjoin=task_dependencies_table.c.dependant_id == id,
            secondaryjoin=task_dependencies_table.c.dependee_id == id,
            backref='dependants')

        # self is after self.prioritize_before's
        # self has lower priority than self.prioritize_before's
        # self is before self.prioritize_after's
        # self has higher priority than self.prioritize_after's
        prioritize_before = db.relationship(
            'DbTask', secondary=task_prioritize_table,
            primaryjoin=task_prioritize_table.c.prioritize_after_id == id,
            secondaryjoin=task_prioritize_table.c.prioritize_before_id == id,
            backref='prioritize_after')

        def __init__(self, summary, description='', is_done=False,
                     is_deleted=False, deadline=None,
                     expected_duration_minutes=None, expected_cost=None,
                     is_public=False, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            db.Model.__init__(self)
            TaskBase.__init__(
                self, summary=summary, description=description,
                is_done=is_done, is_deleted=is_deleted, deadline=deadline,
                expected_duration_minutes=expected_duration_minutes,
                expected_cost=expected_cost, is_public=is_public)

        @classmethod
        def from_dict(cls, d, lazy=None):
            if lazy:
                raise ValueError('parameter \'lazy\' must be None or empty')
            return super(DbTask, cls).from_dict(d=d, lazy=None)

        def clear_relationships(self):
            self._logger.debug('%s', self)
            self.parent = None
            self.children = []
            self.tags = []
            self.users = []
            self.notes = []
            self.attachments = []
            self.dependees = []
            self.dependants = []
            self.prioritize_before = []
            self.prioritize_after = []
Пример #9
0
class SqlAlchemyPersistenceLayer(object):
    _logger = logging_util.get_logger_by_name(__name__,
                                              'SqlAlchemyPersistenceLayer')

    def __init__(self, db):
        self.db = db

        tags_tasks_table = db.Table(
            'tags_tasks',
            db.Column('tag_id',
                      db.Integer,
                      db.ForeignKey('tag.id'),
                      primary_key=True),
            db.Column('task_id',
                      db.Integer,
                      db.ForeignKey('task.id'),
                      primary_key=True))
        self.tags_tasks_table = tags_tasks_table

        self.DbTag = generate_tag_class(db, tags_tasks_table)

        users_tasks_table = db.Table(
            'users_tasks',
            db.Column('user_id',
                      db.Integer,
                      db.ForeignKey('user.id'),
                      index=True),
            db.Column('task_id',
                      db.Integer,
                      db.ForeignKey('task.id'),
                      index=True))
        self.users_tasks_table = users_tasks_table

        task_dependencies_table = db.Table(
            'task_dependencies',
            db.Column('dependee_id',
                      db.Integer,
                      db.ForeignKey('task.id'),
                      primary_key=True),
            db.Column('dependant_id',
                      db.Integer,
                      db.ForeignKey('task.id'),
                      primary_key=True))
        self.task_dependencies_table = task_dependencies_table

        task_prioritize_table = db.Table(
            'task_prioritize',
            db.Column('prioritize_before_id',
                      db.Integer,
                      db.ForeignKey('task.id'),
                      primary_key=True),
            db.Column('prioritize_after_id',
                      db.Integer,
                      db.ForeignKey('task.id'),
                      primary_key=True))
        self.task_prioritize_table = task_prioritize_table

        self.DbTask = generate_task_class(self, tags_tasks_table,
                                          users_tasks_table,
                                          task_dependencies_table,
                                          task_prioritize_table)
        self.DbNote = generate_note_class(db)
        self.DbAttachment = generate_attachment_class(db)
        self.DbUser = generate_user_class(db, users_tasks_table)
        self.DbOption = generate_option_class(db)

        self._db_by_domain = {}
        self._domain_by_db = {}

    def add(self, dbobj):
        self._logger.debug('begin, dbobj: %s', dbobj)
        if not self._is_db_object(dbobj):
            raise Exception(
                'The object is not compatible with the PL: {}'.format(dbobj))

        self.db.session.add(dbobj)
        self._logger.debug('end')

    def delete(self, dbobj):
        self._logger.debug('begin, dbobj: %s', dbobj)
        if not self._is_db_object(dbobj):
            raise Exception(
                'The object is not compatible with the PL: {}'.format(dbobj))
        dbobj.clear_relationships()
        self.db.session.delete(dbobj)
        self._logger.debug('end')

    def commit(self):
        self._logger.debug('begin')
        ###############
        self._logger.debug('committing the db session/transaction')
        self.db.session.commit()
        self._logger.debug('committed the db session/transaction')
        ###############
        self._logger.debug('end')

    def rollback(self):
        self._logger.debug('begin')
        self.db.session.rollback()
        self._logger.debug('end')

    def _is_db_object(self, obj):
        return isinstance(obj, self.db.Model)

    def create_all(self):
        self.db.create_all()

    UNSPECIFIED = object()

    ASCENDING = object()
    DESCENDING = object()

    TASK_ID = object()
    ORDER_NUM = object()
    DEADLINE = object()

    def get_db_field_by_order_field(self, f):
        if f is self.ORDER_NUM:
            return self.DbTask.order_num
        if f is self.TASK_ID:
            return self.DbTask.id
        if f is self.DEADLINE:
            return self.DbTask.deadline
        raise Exception('Unhandled order_by field: {}'.format(f))

    @property
    def task_query(self):
        return self.DbTask.query

    def create_task(self,
                    summary,
                    description='',
                    is_done=False,
                    is_deleted=False,
                    deadline=None,
                    expected_duration_minutes=None,
                    expected_cost=None,
                    is_public=False,
                    lazy=None):
        return self.DbTask(summary=summary,
                           description=description,
                           is_done=is_done,
                           is_deleted=is_deleted,
                           deadline=deadline,
                           expected_duration_minutes=expected_duration_minutes,
                           expected_cost=expected_cost,
                           is_public=is_public,
                           lazy=lazy)

    def get_task(self, task_id):
        return self._get_db_task(task_id)

    def _get_db_task(self, task_id):
        if task_id is None:
            return None
        return self.task_query.get(task_id)

    def _get_tasks_query(self,
                         is_done=UNSPECIFIED,
                         is_deleted=UNSPECIFIED,
                         parent_id=UNSPECIFIED,
                         parent_id_in=UNSPECIFIED,
                         users_contains=UNSPECIFIED,
                         task_id_in=UNSPECIFIED,
                         task_id_not_in=UNSPECIFIED,
                         deadline_is_not_none=False,
                         tags_contains=UNSPECIFIED,
                         is_public=UNSPECIFIED,
                         is_public_or_users_contains=UNSPECIFIED,
                         summary_description_search_term=UNSPECIFIED,
                         order_num_greq_than=UNSPECIFIED,
                         order_num_lesseq_than=UNSPECIFIED,
                         order_by=UNSPECIFIED,
                         limit=UNSPECIFIED):
        """order_by is a list of order directives. Each such directive is
         either a field (e.g. ORDER_NUM) or a sequence of field and direction
          (e.g. [ORDER_NUM, ASCENDING]). Default direction is ASCENDING if not
           specified."""

        query = self.task_query

        if is_done is not self.UNSPECIFIED:
            query = query.filter_by(is_done=is_done)

        if is_deleted is not self.UNSPECIFIED:
            query = query.filter_by(is_deleted=is_deleted)

        if is_public is not self.UNSPECIFIED:
            query = query.filter_by(is_public=is_public)

        if parent_id is not self.UNSPECIFIED:
            if parent_id is None:
                query = query.filter(self.DbTask.parent_id.is_(None))
            else:
                query = query.filter_by(parent_id=parent_id)

        if parent_id_in is not self.UNSPECIFIED:
            if parent_id_in:
                query = query.filter(self.DbTask.parent_id.in_(parent_id_in))
            else:
                # avoid performance penalty
                query = query.filter(False)

        if users_contains is not self.UNSPECIFIED:
            query = query.filter(self.DbTask.users.contains(users_contains))

        if is_public_or_users_contains is not self.UNSPECIFIED:
            db_user = is_public_or_users_contains
            query_m1 = query.outerjoin(
                self.users_tasks_table,
                self.users_tasks_table.c.task_id == self.DbTask.id)
            query_m2 = query_m1.filter(
                or_(self.users_tasks_table.c.user_id == db_user.id,
                    self.DbTask.is_public))
            query = query_m2

        if task_id_in is not self.UNSPECIFIED:
            # Using in_ on an empty set works but is expensive for some db
            # engines. In the case of an empty collection, just use a query
            # that always returns an empty set, without the performance
            # penalty.
            if task_id_in:
                query = query.filter(self.DbTask.id.in_(task_id_in))
            else:
                query = query.filter(False)

        if task_id_not_in is not self.UNSPECIFIED:
            # Using notin_ on an empty set works but is expensive for some db
            # engines. Moreover, it doesn't affect the actual set of selected
            # rows. In the case of an empty collection, just use the same query
            # object again, so we won't incur the performance penalty.
            if task_id_not_in:
                query = query.filter(self.DbTask.id.notin_(task_id_not_in))
            else:
                query = query

        if deadline_is_not_none:
            query = query.filter(self.DbTask.deadline.isnot(None))

        if tags_contains is not self.UNSPECIFIED:
            query = query.filter(self.DbTask.tags.contains(tags_contains))

        if summary_description_search_term is not self.UNSPECIFIED:
            like_term = '%{}%'.format(summary_description_search_term)
            query = query.filter(
                self.DbTask.summary.ilike(like_term)
                | self.DbTask.description.ilike(like_term))

        if order_num_greq_than is not self.UNSPECIFIED:
            query = query.filter(self.DbTask.order_num >= order_num_greq_than)

        if order_num_lesseq_than is not self.UNSPECIFIED:
            query = query.filter(
                self.DbTask.order_num <= order_num_lesseq_than)

        if order_by is not self.UNSPECIFIED:
            if not is_iterable(order_by):
                db_field = self.get_db_field_by_order_field(order_by)
                query = query.order_by(db_field)
            else:
                for ordering in order_by:
                    direction = self.ASCENDING
                    if is_iterable(ordering):
                        order_field = ordering[0]
                        if len(ordering) > 1:
                            direction = ordering[1]
                    else:
                        order_field = ordering
                    db_field = self.get_db_field_by_order_field(order_field)
                    if direction is self.ASCENDING:
                        query = query.order_by(db_field.asc())
                    elif direction is self.DESCENDING:
                        query = query.order_by(db_field.desc())
                    else:
                        raise Exception(
                            'Unknown order_by direction: {}'.format(direction))

        if limit is not self.UNSPECIFIED:
            query = query.limit(limit)

        return query

    def get_tasks(self,
                  is_done=UNSPECIFIED,
                  is_deleted=UNSPECIFIED,
                  parent_id=UNSPECIFIED,
                  parent_id_in=UNSPECIFIED,
                  users_contains=UNSPECIFIED,
                  task_id_in=UNSPECIFIED,
                  task_id_not_in=UNSPECIFIED,
                  deadline_is_not_none=False,
                  tags_contains=UNSPECIFIED,
                  is_public=UNSPECIFIED,
                  is_public_or_users_contains=UNSPECIFIED,
                  summary_description_search_term=UNSPECIFIED,
                  order_num_greq_than=UNSPECIFIED,
                  order_num_lesseq_than=UNSPECIFIED,
                  order_by=UNSPECIFIED,
                  limit=UNSPECIFIED):
        query = self._get_tasks_query(
            is_done=is_done,
            is_deleted=is_deleted,
            parent_id=parent_id,
            parent_id_in=parent_id_in,
            users_contains=users_contains,
            task_id_in=task_id_in,
            task_id_not_in=task_id_not_in,
            deadline_is_not_none=deadline_is_not_none,
            tags_contains=tags_contains,
            is_public=is_public,
            is_public_or_users_contains=is_public_or_users_contains,
            summary_description_search_term=summary_description_search_term,
            order_num_greq_than=order_num_greq_than,
            order_num_lesseq_than=order_num_lesseq_than,
            order_by=order_by,
            limit=limit)
        return (_ for _ in query)

    def get_paginated_tasks(self,
                            is_done=UNSPECIFIED,
                            is_deleted=UNSPECIFIED,
                            parent_id=UNSPECIFIED,
                            parent_id_in=UNSPECIFIED,
                            users_contains=UNSPECIFIED,
                            task_id_in=UNSPECIFIED,
                            task_id_not_in=UNSPECIFIED,
                            deadline_is_not_none=False,
                            tags_contains=UNSPECIFIED,
                            is_public=UNSPECIFIED,
                            is_public_or_users_contains=UNSPECIFIED,
                            summary_description_search_term=UNSPECIFIED,
                            order_num_greq_than=UNSPECIFIED,
                            order_num_lesseq_than=UNSPECIFIED,
                            order_by=UNSPECIFIED,
                            limit=UNSPECIFIED,
                            page_num=None,
                            tasks_per_page=None):

        if page_num is not None and not isinstance(page_num, Number):
            raise TypeError('page_num must be a number')
        if page_num is not None and page_num < 1:
            raise ValueError('page_num must be greater than zero')
        if tasks_per_page is not None and not isinstance(
                tasks_per_page, Number):
            raise TypeError('tasks_per_page must be a number')
        if tasks_per_page is not None and tasks_per_page < 1:
            raise ValueError('tasks_per_page must be greater than zero')

        if page_num is None:
            page_num = 1
        if tasks_per_page is None:
            tasks_per_page = 20

        query = self._get_tasks_query(
            is_done=is_done,
            is_deleted=is_deleted,
            parent_id=parent_id,
            parent_id_in=parent_id_in,
            users_contains=users_contains,
            task_id_in=task_id_in,
            task_id_not_in=task_id_not_in,
            deadline_is_not_none=deadline_is_not_none,
            tags_contains=tags_contains,
            is_public=is_public,
            is_public_or_users_contains=is_public_or_users_contains,
            summary_description_search_term=summary_description_search_term,
            order_num_greq_than=order_num_greq_than,
            order_num_lesseq_than=order_num_lesseq_than,
            order_by=order_by,
            limit=limit)
        pager = query.paginate(page=page_num, per_page=tasks_per_page)
        items = list(pager.items)
        return Pager(page=pager.page,
                     per_page=pager.per_page,
                     items=items,
                     total=pager.total,
                     num_pages=pager.pages,
                     _pager=pager)

    def count_tasks(self,
                    is_done=UNSPECIFIED,
                    is_deleted=UNSPECIFIED,
                    parent_id=UNSPECIFIED,
                    parent_id_in=UNSPECIFIED,
                    users_contains=UNSPECIFIED,
                    task_id_in=UNSPECIFIED,
                    task_id_not_in=UNSPECIFIED,
                    deadline_is_not_none=False,
                    tags_contains=UNSPECIFIED,
                    is_public=UNSPECIFIED,
                    is_public_or_users_contains=UNSPECIFIED,
                    summary_description_search_term=UNSPECIFIED,
                    order_num_greq_than=UNSPECIFIED,
                    order_num_lesseq_than=UNSPECIFIED,
                    order_by=UNSPECIFIED,
                    limit=UNSPECIFIED):
        return self._get_tasks_query(
            is_done=is_done,
            is_deleted=is_deleted,
            parent_id=parent_id,
            parent_id_in=parent_id_in,
            users_contains=users_contains,
            task_id_in=task_id_in,
            task_id_not_in=task_id_not_in,
            deadline_is_not_none=deadline_is_not_none,
            tags_contains=tags_contains,
            is_public=is_public,
            is_public_or_users_contains=is_public_or_users_contains,
            summary_description_search_term=summary_description_search_term,
            order_num_greq_than=order_num_greq_than,
            order_num_lesseq_than=order_num_lesseq_than,
            order_by=order_by,
            limit=limit).count()

    @property
    def tag_query(self):
        return self.DbTag.query

    def create_tag(self, value, description=None, lazy=None):
        return self.DbTag(value=value, description=description, lazy=lazy)

    def _get_db_tag(self, tag_id):
        if tag_id is None:
            raise ValueError('tag_id cannot be None')
        return self.tag_query.get(tag_id)

    def get_tag(self, tag_id):
        if tag_id is None:
            raise ValueError('tag_id cannot be None')
        return self._get_db_tag(tag_id)

    def _get_tags_query(self, value=UNSPECIFIED, limit=None):
        query = self.DbTag.query
        if value is not self.UNSPECIFIED:
            query = query.filter_by(value=value)
        if limit is not None:
            query = query.limit(limit)
        return query

    def get_tags(self, value=UNSPECIFIED, limit=None):
        query = self._get_tags_query(value=value, limit=limit)
        return (_ for _ in query)

    def count_tags(self, value=UNSPECIFIED, limit=None):
        return self._get_tags_query(value=value, limit=limit).count()

    def get_tag_by_value(self, value):
        return self._get_tags_query(value=value).first()

    @property
    def note_query(self):
        return self.DbNote.query

    def create_note(self, content, timestamp=None, lazy=None):
        return self.DbNote(content=content, timestamp=timestamp, lazy=lazy)

    def _get_db_note(self, note_id):
        if note_id is None:
            raise ValueError('note_id acannot be None')
        return self.note_query.get(note_id)

    def get_note(self, note_id):
        if note_id is None:
            raise ValueError('note_id acannot be None')
        return self._get_db_note(note_id)

    def _get_notes_query(self, note_id_in=UNSPECIFIED):
        query = self.note_query
        if note_id_in is not self.UNSPECIFIED:
            if note_id_in:
                query = query.filter(self.DbNote.id.in_(note_id_in))
            else:
                # performance improvement
                query = query.filter(False)
        return query

    def get_notes(self, note_id_in=UNSPECIFIED):
        query = self._get_notes_query(note_id_in=note_id_in)
        return (_ for _ in query)

    def count_notes(self, note_id_in=UNSPECIFIED):
        return self._get_notes_query(note_id_in=note_id_in).count()

    @property
    def attachment_query(self):
        return self.DbAttachment.query

    def create_attachment(self,
                          path,
                          description=None,
                          timestamp=None,
                          filename=None,
                          lazy=None):
        return self.DbAttachment(path=path,
                                 description=description,
                                 timestamp=timestamp,
                                 filename=filename,
                                 lazy=lazy)

    def _get_db_attachment(self, attachment_id):
        if attachment_id is None:
            raise ValueError('attachment_id acannot be None')
        return self.attachment_query.get(attachment_id)

    def get_attachment(self, attachment_id):
        if attachment_id is None:
            raise ValueError('attachment_id acannot be None')
        return self._get_db_attachment(attachment_id)

    def _get_attachments_query(self, attachment_id_in=UNSPECIFIED):
        query = self.attachment_query
        if attachment_id_in is not self.UNSPECIFIED:
            if attachment_id_in:
                query = query.filter(
                    self.DbAttachment.id.in_(attachment_id_in))
            else:
                query = query.filter(False)
        return query

    def get_attachments(self, attachment_id_in=UNSPECIFIED):
        query = self._get_attachments_query(attachment_id_in=attachment_id_in)
        return (_ for _ in query)

    def count_attachments(self, attachment_id_in=UNSPECIFIED):
        return self._get_attachments_query(
            attachment_id_in=attachment_id_in).count()

    @property
    def user_query(self):
        return self.DbUser.query

    def create_user(self,
                    email,
                    hashed_password=None,
                    is_admin=False,
                    lazy=None):
        return self.DbUser(email=email,
                           hashed_password=hashed_password,
                           is_admin=is_admin,
                           lazy=lazy)

    _guest_user = None

    def get_guest_user(self):
        if self._guest_user is None:
            self._guest_user = self.create_user('guest@guest')
            self._guest_user._is_authenticated = False
            self._guest_user._is_anonymous = True
            self._guest_user.is_admin = False
        return self._guest_user

    def _get_db_user(self, user_id):
        if user_id is None:
            raise ValueError('user_id acannot be None')
        return self.user_query.get(user_id)

    def get_user(self, user_id):
        if user_id is None:
            raise ValueError('user_id acannot be None')
        return self._get_db_user(user_id)

    def get_user_by_email(self, email):
        return self.user_query.filter_by(email=email).first()

    def _get_users_query(self, email_in=UNSPECIFIED):
        query = self.user_query
        if email_in is not self.UNSPECIFIED:
            if email_in:
                query = query.filter(self.DbUser.email.in_(email_in))
            else:
                # avoid performance penalty
                query = query.filter(False)
        return query

    def get_users(self, email_in=UNSPECIFIED):
        query = self._get_users_query(email_in=email_in)
        return (_ for _ in query)

    def count_users(self, email_in=UNSPECIFIED):
        return self._get_users_query(email_in=email_in).count()

    @property
    def option_query(self):
        return self.DbOption.query

    def create_option(self, key, value):
        return self.DbOption(key=key, value=value)

    def _get_db_option(self, key):
        if key is None:
            raise ValueError('key acannot be None')
        return self.option_query.get(key)

    def get_option(self, key):
        if key is None:
            raise ValueError('key acannot be None')
        return self._get_db_option(key)

    def _get_options_query(self, key_in=UNSPECIFIED):
        query = self.option_query
        if key_in is not self.UNSPECIFIED:
            if key_in:
                query = query.filter(self.DbOption.key.in_(key_in))
            else:
                # avoid performance penalty
                query = query.filter(False)
        return query

    def get_options(self, key_in=UNSPECIFIED):
        query = self._get_options_query(key_in=key_in)
        return (_ for _ in query)

    def count_options(self, key_in=UNSPECIFIED):
        return self._get_options_query(key_in=key_in).count()
Пример #10
0
class InterlinkedChildren(OneToManySet):
    __change_field__ = TaskBase.FIELD_CHILDREN
    __attr_counterpart__ = 'parent'
    _logger = logging_util.get_logger_by_name(__name__, 'InterlinkedChildren')
Пример #11
0
class InterlinkedTasks(ManyToManySet):
    __change_field__ = UserBase.FIELD_TASKS
    __attr_counterpart__ = 'users'
    _logger = logging_util.get_logger_by_name(__name__,
                                              'InterlinkedTasks')
Пример #12
0
class GenericTask(TaskBase):
    id = None
    summary = None
    description = None
    is_done = None
    is_deleted = None
    deadline = None
    expected_duration_minutes = None
    expected_cost = None
    order_num = None
    parent = None
    children = None
    dependees = None
    dependants = None
    prioritize_before = None
    prioritize_after = None
    tags = None
    users = None
    notes = None
    attachments = None
    is_public = None

    _logger = logging_util.get_logger_by_name(__name__, 'Task')

    def __init__(self,
                 summary=None,
                 description=None,
                 is_done=None,
                 is_deleted=None,
                 deadline=None,
                 expected_duration_minutes=None,
                 expected_cost=None,
                 is_public=None,
                 id=None,
                 parent=None,
                 children=None,
                 dependees=None,
                 dependants=None,
                 prioritize_before=None,
                 prioritize_after=None,
                 tags=None,
                 users=None,
                 notes=None,
                 attachments=None):
        super(GenericTask, self).__init__(
            summary=summary,
            description=description,
            is_done=is_done,
            is_deleted=is_deleted,
            deadline=deadline,
            expected_duration_minutes=expected_duration_minutes,
            expected_cost=expected_cost,
            is_public=is_public)
        self.id = id
        self.parent = parent

        self.children = []
        self.dependees = []
        self.dependants = []
        self.prioritize_before = []
        self.prioritize_after = []
        self.tags = []
        self.users = []
        self.notes = []
        self.attachments = []

        if children:
            self.children.extend(children)
        if dependees:
            self.dependees.extend(dependees)
        if dependants:
            self.dependants.extend(dependants)
        if prioritize_before:
            self.prioritize_before.extend(prioritize_before)
        if prioritize_after:
            self.prioritize_after.extend(prioritize_after)
        if tags:
            self.tags.extend(tags)
        if users:
            self.users.extend(users)
        if notes:
            self.notes.extend(notes)
        if attachments:
            self.attachments.extend(attachments)
Пример #13
0
class InterlinkedAttachments(OneToManySet):
    __change_field__ = TaskBase.FIELD_ATTACHMENTS
    __attr_counterpart__ = 'task'
    _logger = logging_util.get_logger_by_name(__name__,
                                              'InterlinkedAttachments')
Пример #14
0
class User(Changeable, UserBase):
    _logger = logging_util.get_logger_by_name(__name__, 'User')

    _id = None
    _email = None
    _hashed_password = None
    _is_admin = None

    def __init__(self, email, hashed_password=None, is_admin=False, lazy=None):
        super(User, self).__init__(email=email,
                                   hashed_password=hashed_password,
                                   is_admin=is_admin)

        if lazy is None:
            lazy = {}

        self._tasks = InterlinkedTasks(self, lazy=lazy.get('tasks'))

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, value):
        if value != self._id:
            self._on_attr_changing(self.FIELD_ID, self._id)
            self._id = value
            self._on_attr_changed(self.FIELD_ID, self.OP_SET, self._id)

    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if value != self._email:
            self._on_attr_changing(self.FIELD_EMAIL, self._email)
            self._email = value
            self._on_attr_changed(self.FIELD_EMAIL, self.OP_SET, self._email)

    @property
    def hashed_password(self):
        return self._hashed_password

    @hashed_password.setter
    def hashed_password(self, value):
        if value != self._hashed_password:
            self._on_attr_changing(self.FIELD_HASHED_PASSWORD,
                                   self._hashed_password)
            self._hashed_password = value
            self._on_attr_changed(self.FIELD_HASHED_PASSWORD, self.OP_SET,
                                  self._hashed_password)

    @property
    def is_admin(self):
        return self._is_admin

    @is_admin.setter
    def is_admin(self, value):
        if value != self._is_admin:
            self._on_attr_changing(self.FIELD_IS_ADMIN, self._is_admin)
            self._is_admin = value
            self._on_attr_changed(self.FIELD_IS_ADMIN, self.OP_SET,
                                  self._is_admin)

    @property
    def tasks(self):
        self._logger.debug('%s', self)
        return self._tasks

    def clear_relationships(self):
        self.tasks.clear()
Пример #15
0
class InMemoryPersistenceLayer(object):
    _logger = logging_util.get_logger_by_name(__name__,
                                              'InMemoryPersistenceLayer')

    def __init__(self):
        self._added_objects = set()
        self._deleted_objects = set()
        self._changed_objects = set()
        self._values_by_object = {}

        self._tasks = []  # TODO: change these to sets
        self._tasks_by_id = {}

        self._tags = []
        self._tags_by_id = {}
        self._tags_by_value = {}

        self._users = []
        self._users_by_id = {}
        self._users_by_email = {}

        self._options = []
        self._options_by_key = {}

        self._notes = []
        self._notes_by_id = {}

        self._attachments = []
        self._attachments_by_id = {}

    UNSPECIFIED = object()

    ASCENDING = object()
    DESCENDING = object()

    TASK_ID = object()
    ORDER_NUM = object()
    DEADLINE = object()

    def create_all(self):
        pass

    def create_task(self,
                    summary,
                    description='',
                    is_done=False,
                    is_deleted=False,
                    deadline=None,
                    expected_duration_minutes=None,
                    expected_cost=None,
                    is_public=False,
                    lazy=None):
        return Task(summary=summary,
                    description=description,
                    is_done=is_done,
                    is_deleted=is_deleted,
                    deadline=deadline,
                    expected_duration_minutes=expected_duration_minutes,
                    expected_cost=expected_cost,
                    is_public=is_public,
                    lazy=lazy)

    def get_task(self, task_id):
        return self._tasks_by_id.get(task_id)

    def get_tasks(self,
                  is_done=UNSPECIFIED,
                  is_deleted=UNSPECIFIED,
                  parent_id=UNSPECIFIED,
                  parent_id_in=UNSPECIFIED,
                  users_contains=UNSPECIFIED,
                  task_id_in=UNSPECIFIED,
                  task_id_not_in=UNSPECIFIED,
                  deadline_is_not_none=False,
                  tags_contains=UNSPECIFIED,
                  is_public=UNSPECIFIED,
                  is_public_or_users_contains=UNSPECIFIED,
                  summary_description_search_term=UNSPECIFIED,
                  order_num_greq_than=UNSPECIFIED,
                  order_num_lesseq_than=UNSPECIFIED,
                  order_by=UNSPECIFIED,
                  limit=UNSPECIFIED):

        query = self._tasks

        if is_done is not self.UNSPECIFIED:
            query = (_ for _ in query if _.is_done == is_done)

        if is_deleted is not self.UNSPECIFIED:
            query = (_ for _ in query if _.is_deleted == is_deleted)

        if is_public is not self.UNSPECIFIED:
            query = (_ for _ in query if _.is_public == is_public)

        if parent_id is not self.UNSPECIFIED:
            if parent_id is None:
                query = (_ for _ in query if _.parent_id is None)
            else:
                query = (_ for _ in query if _.parent_id == parent_id)

        if parent_id_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.parent_id in parent_id_in)

        if users_contains is not self.UNSPECIFIED:
            query = (_ for _ in query if users_contains in _.users)

        if is_public_or_users_contains is not self.UNSPECIFIED:
            query = (_ for _ in query
                     if _.is_public or is_public_or_users_contains in _.users)

        if task_id_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.id in task_id_in)

        if task_id_not_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.id not in task_id_not_in)

        if deadline_is_not_none:
            query = (_ for _ in query if _.deadline is not None)

        if tags_contains is not self.UNSPECIFIED:
            query = (_ for _ in query if tags_contains in _.tags)

        if summary_description_search_term is not self.UNSPECIFIED:
            term = summary_description_search_term
            term_lower = term.casefold()
            query = (_ for _ in query if term_lower in _.summary.casefold()
                     or term_lower in _.description.casefold())

        if order_num_greq_than is not self.UNSPECIFIED:
            query = (_ for _ in query if _.order_num >= order_num_greq_than)

        if order_num_lesseq_than is not self.UNSPECIFIED:
            query = (_ for _ in query if _.order_num <= order_num_lesseq_than)

        if order_by is not self.UNSPECIFIED:
            if not is_iterable(order_by):
                sort_key = self._get_sort_key_by_order_field(order_by)
                query = sorted(query, key=sort_key)
            else:
                for ordering in order_by:
                    direction = self.ASCENDING
                    if is_iterable(ordering):
                        order_field = ordering[0]
                        if len(ordering) > 1:
                            direction = ordering[1]
                    else:
                        order_field = ordering
                    sort_key = self._get_sort_key_by_order_field(order_field)
                    if direction is self.ASCENDING:
                        query = sorted(query, key=sort_key)
                    elif direction is self.DESCENDING:
                        query = sorted(query, key=sort_key, reverse=True)
                    else:
                        raise Exception(
                            'Unknown order_by direction: {}'.format(direction))

        if limit is not self.UNSPECIFIED and limit >= 0:
            query = islice(query, limit)

        return query

    def _get_sort_key_by_order_field(self, order_by):
        if order_by is self.ORDER_NUM:
            return lambda task: task.order_num
        if order_by is self.TASK_ID:
            return lambda task: task.id
        if order_by is self.DEADLINE:
            return lambda task: task.deadline
        raise Exception('Unhandled order_by field: {}'.format(order_by))

    def get_paginated_tasks(self,
                            is_done=UNSPECIFIED,
                            is_deleted=UNSPECIFIED,
                            parent_id=UNSPECIFIED,
                            parent_id_in=UNSPECIFIED,
                            users_contains=UNSPECIFIED,
                            task_id_in=UNSPECIFIED,
                            task_id_not_in=UNSPECIFIED,
                            deadline_is_not_none=False,
                            tags_contains=UNSPECIFIED,
                            is_public=UNSPECIFIED,
                            is_public_or_users_contains=UNSPECIFIED,
                            summary_description_search_term=UNSPECIFIED,
                            order_num_greq_than=UNSPECIFIED,
                            order_num_lesseq_than=UNSPECIFIED,
                            order_by=UNSPECIFIED,
                            limit=UNSPECIFIED,
                            page_num=None,
                            tasks_per_page=None):

        if page_num is not None and not isinstance(page_num, Number):
            raise TypeError('page_num must be a number')
        if page_num is not None and page_num < 1:
            raise ValueError('page_num must be greater than zero')
        if tasks_per_page is not None and not isinstance(
                tasks_per_page, Number):
            raise TypeError('tasks_per_page must be a number')
        if tasks_per_page is not None and tasks_per_page < 1:
            raise ValueError('tasks_per_page must be greater than zero')

        if page_num is None:
            page_num = 1
        if tasks_per_page is None:
            tasks_per_page = 20

        query = self.get_tasks(
            is_done=is_done,
            is_deleted=is_deleted,
            parent_id=parent_id,
            parent_id_in=parent_id_in,
            users_contains=users_contains,
            task_id_in=task_id_in,
            task_id_not_in=task_id_not_in,
            deadline_is_not_none=deadline_is_not_none,
            tags_contains=tags_contains,
            is_public=is_public,
            is_public_or_users_contains=is_public_or_users_contains,
            summary_description_search_term=summary_description_search_term,
            order_num_greq_than=order_num_greq_than,
            order_num_lesseq_than=order_num_lesseq_than,
            order_by=order_by,
            limit=limit)
        tasks = list(query)
        start_task = (page_num - 1) * tasks_per_page
        items = list(islice(tasks, start_task, start_task + tasks_per_page))
        total_tasks = len(tasks)
        num_pages = total_tasks // tasks_per_page
        if total_tasks % tasks_per_page > 0:
            num_pages += 1
        return Pager(page=page_num,
                     per_page=tasks_per_page,
                     items=items,
                     total=total_tasks,
                     num_pages=num_pages,
                     _pager=None)

    def count_tasks(self,
                    is_done=UNSPECIFIED,
                    is_deleted=UNSPECIFIED,
                    parent_id=UNSPECIFIED,
                    parent_id_in=UNSPECIFIED,
                    users_contains=UNSPECIFIED,
                    task_id_in=UNSPECIFIED,
                    task_id_not_in=UNSPECIFIED,
                    deadline_is_not_none=False,
                    tags_contains=UNSPECIFIED,
                    is_public=UNSPECIFIED,
                    is_public_or_users_contains=UNSPECIFIED,
                    summary_description_search_term=UNSPECIFIED,
                    order_num_greq_than=UNSPECIFIED,
                    order_num_lesseq_than=UNSPECIFIED,
                    order_by=UNSPECIFIED,
                    limit=UNSPECIFIED):

        return len(
            list(
                self.get_tasks(
                    is_done=is_done,
                    is_deleted=is_deleted,
                    parent_id=parent_id,
                    parent_id_in=parent_id_in,
                    users_contains=users_contains,
                    task_id_in=task_id_in,
                    task_id_not_in=task_id_not_in,
                    deadline_is_not_none=deadline_is_not_none,
                    tags_contains=tags_contains,
                    is_public=is_public,
                    is_public_or_users_contains=is_public_or_users_contains,
                    summary_description_search_term=
                    summary_description_search_term,
                    order_num_greq_than=order_num_greq_than,
                    order_num_lesseq_than=order_num_lesseq_than,
                    order_by=order_by,
                    limit=limit)))

    def create_tag(self, value, description=None, lazy=None):
        return Tag(value=value, description=description, lazy=lazy)

    def get_tag(self, tag_id):
        if tag_id is None:
            raise ValueError('No tag_id provided.')
        return self._tags_by_id.get(tag_id)

    def get_tag_by_value(self, value):
        return self._tags_by_value.get(value)

    def get_tags(self, value=UNSPECIFIED, limit=None):
        query = self._tags
        if value is not self.UNSPECIFIED:
            query = (_ for _ in query if _.value == value)
        if limit is not None:
            query = islice(query, limit)
        return query

    def count_tags(self, value=UNSPECIFIED, limit=None):
        return len(list(self.get_tags(value=value, limit=limit)))

    def create_attachment(self,
                          path,
                          description=None,
                          timestamp=None,
                          filename=None,
                          lazy=None):
        return Attachment(path=path,
                          description=description,
                          timestamp=timestamp,
                          filename=filename,
                          lazy=lazy)

    def get_attachment(self, attachment_id):
        if attachment_id is None:
            raise ValueError('No attachment_id provided.')
        return self._attachments_by_id.get(attachment_id)

    def get_attachments(self, attachment_id_in=UNSPECIFIED):
        query = (_ for _ in self._attachments)
        if attachment_id_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.id in attachment_id_in)
        return query

    def count_attachments(self, attachment_id_in=UNSPECIFIED):
        return len(
            list(self.get_attachments(attachment_id_in=attachment_id_in)))

    def create_note(self, content, timestamp=None, lazy=None):
        return Note(content=content, timestamp=timestamp, lazy=lazy)

    def get_note(self, note_id):
        if note_id is None:
            raise ValueError('No note_id provided.')
        return self._notes_by_id.get(note_id)

    def get_notes(self, note_id_in=UNSPECIFIED):
        query = self._notes
        if note_id_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.id in note_id_in)
        return query

    def count_notes(self, note_id_in=UNSPECIFIED):
        return len(list(self.get_notes(note_id_in=note_id_in)))

    def create_option(self, key, value):
        return Option(key=key, value=value)

    def get_option(self, key):
        if key is None:
            raise ValueError('No option_id provided.')
        return self._options_by_key.get(key)

    def get_options(self, key_in=UNSPECIFIED):
        query = self._options
        if key_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.id in key_in)
        return query

    def count_options(self, key_in=UNSPECIFIED):
        return len(list(self.get_options(key_in=key_in)))

    def create_user(self,
                    email,
                    hashed_password=None,
                    is_admin=False,
                    lazy=None):
        return User(email=email,
                    hashed_password=hashed_password,
                    is_admin=is_admin,
                    lazy=lazy)

    _guest_user = None

    def get_guest_user(self):
        if self._guest_user is None:
            self._guest_user = self.create_user('guest@guest')
            self._guest_user._is_authenticated = False
            self._guest_user._is_anonymous = True
            self._guest_user.is_admin = False
        return self._guest_user

    def get_user(self, user_id):
        if user_id is None:
            raise ValueError('No user_id provided.')
        return self._users_by_id.get(user_id)

    def get_user_by_email(self, email):
        return self._users_by_email.get(email)

    def get_users(self, email_in=UNSPECIFIED):
        query = self._users
        if email_in is not self.UNSPECIFIED:
            query = (_ for _ in query if _.email in email_in)
        return query

    def count_users(self, email_in=UNSPECIFIED):
        return len(list(self.get_users(email_in=email_in)))

    def add(self, obj):
        if obj in self._added_objects:
            return
        if obj in self._deleted_objects:
            raise Exception(
                'The object (id={}) has already been deleted.'.format(obj.id))
        if (obj in self._tasks or obj in self._tags or obj in self._notes
                or obj in self._attachments or obj in self._users
                or obj in self._options):
            return
        d = obj.to_dict()
        self._values_by_object[obj] = d
        self._added_objects.add(obj)

    def delete(self, obj):
        if obj in self._deleted_objects:
            return
        if obj in self._added_objects:
            raise Exception(
                'The object (id={}) has already been added.'.format(obj.id))
        self._deleted_objects.add(obj)

    def commit(self):
        for domobj in list(self._added_objects):
            tt = self._get_object_type(domobj)
            if tt != ObjectTypes.Option and domobj.id is None:
                domobj.id = self._get_next_id(tt)
            if tt == ObjectTypes.Task and domobj.order_num is None:
                domobj.order_num = 0

            if tt == ObjectTypes.Attachment:
                if domobj.id in self._attachments_by_id:
                    raise Exception(
                        'There already exists an attachment with id '
                        '{}'.format(domobj.id))
                self._attachments.append(domobj)
                self._attachments_by_id[domobj.id] = domobj
            elif tt == ObjectTypes.Note:
                if domobj.id in self._notes_by_id:
                    raise Exception(
                        'There already exists a note with id {}'.format(
                            domobj.id))
                self._notes.append(domobj)
                self._notes_by_id[domobj.id] = domobj
            elif tt == ObjectTypes.Task:
                if domobj.id in self._tasks_by_id:
                    raise Exception(
                        'There already exists a task with id {}'.format(
                            domobj.id))
                self._tasks.append(domobj)
                self._tasks_by_id[domobj.id] = domobj
            elif tt == ObjectTypes.Tag:
                if domobj.id in self._tags_by_id:
                    raise Exception(
                        'There already exists a tag with id {}'.format(
                            domobj.id))
                if domobj.value in self._tags_by_value:
                    raise Exception(
                        'There already exists a tag with value "{}"'.format(
                            domobj.value))
                self._tags.append(domobj)
                self._tags_by_id[domobj.id] = domobj
                self._tags_by_value[domobj.value] = domobj
            elif tt == ObjectTypes.Option:
                if domobj.key in self._options_by_key:
                    raise Exception(
                        'There already exists an option with key {}'.format(
                            domobj.id))
                self._options.append(domobj)
                self._options_by_key[domobj.id] = domobj
            else:  # tt == ObjectTypes.User
                if domobj.id in self._users_by_id:
                    raise Exception(
                        'There already exists a user with id {}'.format(
                            domobj.id))
                if domobj.email in self._users_by_email:
                    raise Exception(
                        'There already exists a user with email "{}"'.format(
                            domobj.email))
                self._users.append(domobj)
                self._users_by_id[domobj.id] = domobj
                self._users_by_email[domobj.email] = domobj
            self._values_by_object[domobj] = domobj.to_dict()
            self._added_objects.remove(domobj)
        self._added_objects.clear()

        for domobj in list(self._deleted_objects):
            tt = self._get_object_type(domobj)
            domobj.clear_relationships()
            if tt == ObjectTypes.Attachment:
                self._attachments.remove(domobj)
                del self._attachments_by_id[domobj.id]
            elif tt == ObjectTypes.Note:
                self._notes.remove(domobj)
                del self._notes_by_id[domobj.id]
            elif tt == ObjectTypes.Task:
                self._tasks.remove(domobj)
                del self._tasks_by_id[domobj.id]
            elif tt == ObjectTypes.Tag:
                self._tags.remove(domobj)
                del self._tags_by_id[domobj.id]
            elif tt == ObjectTypes.Option:
                self._options.remove(domobj)
                del self._options_by_key[domobj.key]
            else:  # tt == ObjectTypes.User
                self._users.remove(domobj)
                del self._users_by_id[domobj.id]
                del self._users_by_email[domobj.email]
            self._deleted_objects.remove(domobj)
        self._deleted_objects.clear()

        def _process_changed_attr(domobj, new_values, type_name, attr_name,
                                  collection):
            old_value = self._values_by_object[domobj][attr_name]
            new_value = new_values[attr_name]
            if old_value != new_value:
                if new_value in collection:
                    raise Exception(
                        'There already exists a {} with {} "{}"'.format(
                            type_name, attr_name, new_value))
                del collection[old_value]
                collection[new_value] = domobj

        for domobj in self._tasks:
            new_values = domobj.to_dict()
            _process_changed_attr(domobj, new_values, 'task', 'id',
                                  self._tasks_by_id)
            if 'order_num' in new_values and new_values['order_num'] is None:
                raise ValueError(
                    'order_num cannot be None, Task "{}" ({})'.format(
                        domobj.summary, domobj.id))
            self._values_by_object[domobj] = new_values
        for domobj in self._tags:
            new_values = domobj.to_dict()
            _process_changed_attr(domobj, new_values, 'tag', 'id',
                                  self._tags_by_id)
            _process_changed_attr(domobj, new_values, 'tag', 'value',
                                  self._tags_by_value)
            self._values_by_object[domobj] = new_values
        for domobj in self._notes:
            new_values = domobj.to_dict()
            _process_changed_attr(domobj, new_values, 'note', 'id',
                                  self._notes_by_id)
            self._values_by_object[domobj] = new_values
        for domobj in self._attachments:
            new_values = domobj.to_dict()
            _process_changed_attr(domobj, new_values, 'attachment', 'id',
                                  self._attachments_by_id)
            self._values_by_object[domobj] = new_values
        for domobj in self._users:
            new_values = domobj.to_dict()
            _process_changed_attr(domobj, new_values, 'user', 'id',
                                  self._users_by_id)
            _process_changed_attr(domobj, new_values, 'user', 'email',
                                  self._users_by_email)
            self._values_by_object[domobj] = new_values
        for domobj in self._options:
            new_values = domobj.to_dict()
            _process_changed_attr(domobj, new_values, 'option', 'key',
                                  self._options_by_key)
            self._values_by_object[domobj] = new_values

        self._clear_affected_objects()

    def _get_next_task_id(self):
        if not self._tasks_by_id:
            return 1
        return max(self._tasks_by_id.keys()) + 1

    def _get_next_tag_id(self):
        if not self._tags_by_id:
            return 1
        return max(self._tags_by_id.keys()) + 1

    def _get_next_note_id(self):
        if not self._notes_by_id:
            return 1
        return max(self._notes_by_id.keys()) + 1

    def _get_next_attachment_id(self):
        if not self._attachments_by_id:
            return 1
        return max(self._attachments_by_id.keys()) + 1

    def _get_next_user_id(self):
        if not self._users_by_id:
            return 1
        return max(self._users_by_id.keys()) + 1

    def _get_next_id(self, objtype):
        if objtype == ObjectTypes.Task:
            return self._get_next_task_id()
        if objtype == ObjectTypes.Tag:
            return self._get_next_tag_id()
        if objtype == ObjectTypes.Attachment:
            return self._get_next_attachment_id()
        if objtype == ObjectTypes.Note:
            return self._get_next_note_id()
        if objtype == ObjectTypes.User:
            return self._get_next_user_id()
        raise Exception('Unknown object type: {}'.format(objtype))

    def rollback(self):
        for t, d in self._values_by_object.items():
            t.update_from_dict(d)
        for t in self._added_objects:
            del self._values_by_object[t]
        self._clear_affected_objects()

    def _clear_affected_objects(self):
        self._changed_objects.clear()
        self._added_objects.clear()
        self._deleted_objects.clear()

    def _get_object_type(self, domobj):
        try:
            tt = domobj.object_type
        except AttributeError:
            raise Exception('Not a domain object: {}, {}'.format(
                domobj,
                type(domobj).__name__))
        if tt not in ObjectTypes.all:
            raise Exception('Unknown object type: {}, {}, "{}"'.format(
                domobj,
                type(domobj).__name__, tt))
        return tt
Пример #16
0
class InterlinkedDependants(ManyToManySet):
    __change_field__ = TaskBase.FIELD_DEPENDANTS
    __attr_counterpart__ = 'dependees'
    _logger = logging_util.get_logger_by_name(__name__,
                                              'InterlinkedDependants')
Пример #17
0
class InterlinkedPrioritizeAfter(ManyToManySet):
    __change_field__ = TaskBase.FIELD_PRIORITIZE_AFTER
    __attr_counterpart__ = 'prioritize_before'
    _logger = logging_util.get_logger_by_name(__name__,
                                              'InterlinkedPrioritizeAfter')
Пример #18
0
class Note(Changeable, NoteBase):
    _logger = logging_util.get_logger_by_name(__name__, 'Note')

    _id = None
    _content = ''
    _timestamp = None

    _task = None

    def __init__(self, content, timestamp=None, lazy=None):
        super(Note, self).__init__(content, timestamp)
        self._logger.debug('Note.__init__ %s', self)

        if lazy is None:
            lazy = {}

        self._task_lazy = lazy.get('task')

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, value):
        if value != self._id:
            self._on_attr_changing(self.FIELD_ID, self._id)
            self._id = value
            self._on_attr_changed(self.FIELD_ID, self.OP_SET, self._id)

    @property
    def content(self):
        return self._content

    @content.setter
    def content(self, value):
        if value != self._content:
            self._on_attr_changing(self.FIELD_CONTENT, self._content)
            self._content = value
            self._on_attr_changed(self.FIELD_CONTENT, self.OP_SET,
                                  self._content)

    @property
    def timestamp(self):
        return self._timestamp

    @timestamp.setter
    def timestamp(self, value):
        if value != self._timestamp:
            self._on_attr_changing(self.FIELD_TIMESTAMP, self._timestamp)
            self._timestamp = value
            self._on_attr_changed(self.FIELD_TIMESTAMP, self.OP_SET,
                                  self._timestamp)

    @property
    def task_id(self):
        if self.task:
            return self.task.id
        return None

    def _populate_task(self):
        if self._task_lazy:
            self._logger.debug('populating task from lazy %s', self)
            value = self._task_lazy()
            self._task_lazy = None
            self.task = value

    @property
    def task(self):
        self._populate_task()
        return self._task

    @task.setter
    def task(self, value):
        self._populate_task()
        if value != self._task:
            self._on_attr_changing(self.FIELD_TASK, self._task)
            if self._task is not None:
                self._task.notes.discard(self)
            self._task = value
            if self._task is not None:
                self._task.notes.add(self)
            self._on_attr_changed(self.FIELD_TASK, self.OP_SET, self._task)

    def clear_relationships(self):
        self.task = None
Пример #19
0
class ViewLayer(object):
    _logger = logging_util.get_logger_by_name(__name__, 'ViewLayer')

    def __init__(self, ll, bcrypt, renderer=None, login_src=None):
        self.ll = ll
        if renderer is None:
            renderer = DefaultRenderer()
        self.renderer = renderer
        if login_src is None:
            login_src = DefaultLoginSource(bcrypt)
        self.login_src = login_src

    def render_template(self, template_name, **kwargs):
        return self.renderer.render_template(template_name, **kwargs)

    def make_response(self, *args):
        return self.renderer.make_response(*args)

    def redirect(self, *args, **kwargs):
        return self.renderer.redirect(*args, **kwargs)

    def url_for(self, *args, **kwargs):
        return self.renderer.url_for(*args, **kwargs)

    def send_from_directory(self, *args, **kwargs):
        return self.renderer.send_from_directory(*args, **kwargs)

    def flash(self, *args, **kwargs):
        return self.renderer.flash(*args, **kwargs)

    def login_user(self, *args, **kwargs):
        return self.login_src.login_user(*args, **kwargs)

    def logout_user(self, *args, **kwargs):
        return self.login_src.logout_user(*args, **kwargs)

    def check_password_hash(self, *args, **kwargs):
        return self.login_src.check_password_hash(*args, **kwargs)

    def get_form_or_arg(self, request, name):
        if name in request.form:
            return request.form[name]
        return request.args.get(name)

    def index(self, request, current_user):
        show_deleted = request.cookies.get('show_deleted')
        show_done = request.cookies.get('show_done')
        page_num = None
        try:
            page_num = int(self.get_form_or_arg(request, 'page'))
        except:
            pass
        tasks_per_page = None
        try:
            tasks_per_page = int(self.get_form_or_arg(request, 'per_page'))
        except:
            pass

        data = self.ll.get_index_data(show_deleted,
                                      show_done,
                                      current_user,
                                      page_num=page_num,
                                      tasks_per_page=tasks_per_page)

        resp = self.make_response(
            self.render_template('index.t.html',
                                 show_deleted=data['show_deleted'],
                                 show_done=data['show_done'],
                                 cycle=itertools.cycle,
                                 user=current_user,
                                 tasks=data['tasks'],
                                 tags=data['all_tags'],
                                 pager=data['pager'],
                                 pager_link_page='index',
                                 pager_link_args={}))
        return resp

    def hierarchy(self, request, current_user):
        show_deleted = request.cookies.get('show_deleted')
        show_done = request.cookies.get('show_done')

        data = self.ll.get_index_hierarchy_data(show_deleted, show_done,
                                                current_user)

        resp = self.make_response(
            self.render_template('hierarchy.t.html',
                                 show_deleted=data['show_deleted'],
                                 show_done=data['show_done'],
                                 cycle=itertools.cycle,
                                 user=current_user,
                                 tasks_h=data['tasks_h'],
                                 tags=data['all_tags']))
        return resp

    def deadlines(self, request, current_user):
        data = self.ll.get_deadlines_data(current_user)
        return self.make_response(
            self.render_template('deadlines.t.html',
                                 cycle=itertools.cycle,
                                 deadline_tasks=data['deadline_tasks']))

    def task_new_get(self, request, current_user):
        summary = self.get_form_or_arg(request, 'summary')
        description = self.get_form_or_arg(request, 'description')
        deadline = self.get_form_or_arg(request, 'deadline')
        is_done = self.get_form_or_arg(request, 'is_done')
        is_deleted = self.get_form_or_arg(request, 'is_deleted')
        order_num = self.get_form_or_arg(request, 'order_num')
        expected_duration_minutes = self.get_form_or_arg(
            request, 'expected_duration_minutes')
        expected_cost = self.get_form_or_arg(request, 'expected_cost')
        parent_id = self.get_form_or_arg(request, 'parent_id')
        tags = self.get_form_or_arg(request, 'tags')

        prev_url = self.get_form_or_arg(request, 'prev_url')

        return self.render_template(
            'new_task.t.html',
            prev_url=prev_url,
            summary=summary,
            description=description,
            deadline=deadline,
            is_done=is_done,
            is_deleted=is_deleted,
            order_num=order_num,
            expected_duration_minutes=expected_duration_minutes,
            expected_cost=expected_cost,
            parent_id=parent_id,
            tags=tags)

    def task_new_post(self, request, current_user):
        self._logger.debug('begin')
        summary = self.get_form_or_arg(request, 'summary')
        description = self.get_form_or_arg(request, 'description')
        deadline = self.get_form_or_arg(request, 'deadline') or None
        is_done = self.get_form_or_arg(request, 'is_done') or None
        is_deleted = self.get_form_or_arg(request, 'is_deleted') or None
        order_type = self.get_form_or_arg(request, 'order_type') or 'bottom'
        expected_duration_minutes = self.get_form_or_arg(
            request, 'expected_duration_minutes') or None
        expected_cost = self.get_form_or_arg(request, 'expected_cost') or None
        parent_id = self.get_form_or_arg(request, 'parent_id') or None
        is_public = self.get_form_or_arg(request, 'is_public') or None

        tags = self.get_form_or_arg(request, 'tags')
        if tags:
            tags = [s.strip() for s in tags.split(',')]

        self._logger.debug('calculating order_num')
        if order_type == 'top':
            order_num = self.ll.get_highest_order_num()
            if order_num is not None:
                order_num += 2
            else:
                order_num = 0
        elif order_type == 'order_num':
            order_num = self.get_form_or_arg(request, 'order_num') or None
        else:  # bottom
            order_num = self.ll.get_lowest_order_num()
            if order_num is not None:
                order_num -= 2
            else:
                order_num = 0
        self._logger.debug('calculated order_num: %d', order_num)

        self._logger.debug('creating the new task object via LL')
        task = self.ll.create_new_task(
            summary=summary,
            description=description,
            is_done=is_done,
            is_deleted=is_deleted,
            deadline=deadline,
            order_num=order_num,
            expected_duration_minutes=expected_duration_minutes,
            expected_cost=expected_cost,
            parent_id=parent_id,
            is_public=is_public,
            current_user=current_user)

        if tags:
            for tag_name in tags:
                self._logger.debug('adding tag "%s"', tag_name)
                self.ll.do_add_tag_to_task(task, tag_name, current_user)

        self._logger.debug('getting next_url')
        next_url = self.get_form_or_arg(request, 'next_url')
        if not next_url:
            self._logger.debug('next_url not defined')
            next_url = self.url_for('view_task', id=task.id)

        self._logger.debug('end')
        return self.redirect(next_url)

    def task_mark_done(self, request, current_user, task_id):
        self.ll.task_set_done(task_id, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_mark_undone(self, request, current_user, task_id):
        self.ll.task_unset_done(task_id, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_delete(self, request, current_user, task_id):
        self.ll.task_set_deleted(task_id, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_undelete(self, request, current_user, task_id):
        self.ll.task_unset_deleted(task_id, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_purge(self, request, current_user, task_id):
        task = self.ll.pl_get_task(task_id)
        if not task:
            raise NotFound("No task found for the id '{}'".format(task_id))
        if not task.is_deleted:
            raise BadRequest(
                "Indicated task (id {}) has not been deleted.".format(task_id))
        self.ll.purge_task(task, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def purge_all(self, request, current_user):
        are_you_sure = request.args.get('are_you_sure')
        if are_you_sure:
            self.ll.purge_all_deleted_tasks(current_user)
            return self.redirect(
                request.args.get('next') or self.url_for('index'))
        return self.render_template('purge.t.html')

    def task(self, request, current_user, task_id):
        show_deleted = request.cookies.get('show_deleted')
        show_done = request.cookies.get('show_done')
        try:
            page_num = int(request.args.get('page', 1))
        except Exception:
            page_num = 1
        try:
            tasks_per_page = int(request.args.get('per_page', 20))
        except Exception:
            tasks_per_page = 20
        data = self.ll.get_task_data(task_id,
                                     current_user,
                                     include_deleted=show_deleted,
                                     include_done=show_done,
                                     page_num=page_num,
                                     tasks_per_page=tasks_per_page)

        return self.render_template('task.t.html',
                                    task=data['task'],
                                    descendants=data['descendants'],
                                    cycle=itertools.cycle,
                                    show_deleted=show_deleted,
                                    show_done=show_done,
                                    pager=data['pager'],
                                    pager_link_page='view_task',
                                    pager_link_args={'id': task_id},
                                    current_user=current_user,
                                    ops=TaskUserOps,
                                    show_hierarchy=False)

    def task_hierarchy(self, request, current_user, task_id):
        show_deleted = request.cookies.get('show_deleted')
        show_done = request.cookies.get('show_done')
        data = self.ll.get_task_hierarchy_data(task_id,
                                               current_user,
                                               include_deleted=show_deleted,
                                               include_done=show_done)

        return self.render_template('task.t.html',
                                    task=data['task'],
                                    descendants=data['descendants'],
                                    cycle=itertools.cycle,
                                    show_deleted=show_deleted,
                                    show_done=show_done,
                                    ops=TaskUserOps,
                                    show_hierarchy=True)

    def note_new_post(self, request, current_user):
        if 'task_id' not in request.form:
            return ('No task_id specified', 400)
        task_id = request.form['task_id']
        content = request.form['content']

        self.ll.create_new_note(task_id, content, current_user)

        return self.redirect(self.url_for('view_task', id=task_id))

    def task_edit(self, request, current_user, task_id):
        def render_get_response():
            data = self.ll.get_edit_task_data(task_id, current_user)
            return self.render_template("edit_task.t.html",
                                        task=data['task'],
                                        tag_list=data['tag_list'])

        if request.method == 'GET':
            return render_get_response()

        if 'summary' not in request.form or 'description' not in request.form:
            return render_get_response()

        summary = request.form['summary']
        description = request.form['description']
        deadline = request.form['deadline']

        is_done = ('is_done' in request.form
                   and not not request.form['is_done'])
        is_deleted = ('is_deleted' in request.form
                      and not not request.form['is_deleted'])

        order_num = None
        if 'order_num' in request.form:
            order_num = request.form['order_num']

        parent_id = None
        if 'parent_id' in request.form:
            parent_id = request.form['parent_id']

        is_public = ('is_public' in request.form
                     and not not request.form['is_public'])

        duration = int_from_str(request.form['expected_duration_minutes'])

        expected_cost = money_from_str(request.form['expected_cost'])

        task = self.ll.set_task(task_id, current_user, summary, description,
                                deadline, is_done, is_deleted, order_num,
                                duration, expected_cost, parent_id, is_public)

        return self.redirect(self.url_for('view_task', id=task.id))

    def attachment_new(self, request, current_user, timestamp=None):
        if 'task_id' not in request.form:
            raise BadRequest('No task_id specified')
        task_id = request.form['task_id']

        f = request.files['filename']
        if f is None or not f.filename or not self.ll.allowed_file(f.filename):
            raise BadRequest('Invalid file')

        if 'description' in request.form:
            description = request.form['description']
        else:
            description = ''

        self.ll.create_new_attachment(task_id,
                                      f,
                                      description,
                                      current_user,
                                      timestamp=timestamp)

        return self.redirect(self.url_for('view_task', id=task_id))

    def attachment(self, request, current_user, attachment_id, name):
        att = self.ll.pl_get_attachment(attachment_id)
        if att is None:
            raise NotFound(
                "No attachment found for the id '{}'".format(attachment_id))

        return self.send_from_directory(self.ll.upload_folder, att.path)

    def task_up(self, request, current_user, task_id):
        show_deleted = request.cookies.get('show_deleted')
        self.ll.do_move_task_up(task_id, show_deleted, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_top(self, request, current_user, task_id):
        self.ll.do_move_task_to_top(task_id, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_down(self, request, current_user, task_id):
        show_deleted = request.cookies.get('show_deleted')
        self.ll.do_move_task_down(task_id, show_deleted, current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_bottom(self, request, current_user, task_id):
        self.ll.do_move_task_to_bottom(task_id, current_user)

        return self.redirect(request.args.get('next') or self.url_for('index'))

    def long_order_change(self, request, current_user):
        task_to_move_id = self.get_form_or_arg(request,
                                               'long_order_task_to_move')
        if task_to_move_id is None:
            return self.redirect(
                request.args.get('next') or self.url_for('index'))

        target_id = self.get_form_or_arg(request, 'long_order_target')
        if target_id is None:
            return self.redirect(
                request.args.get('next') or self.url_for('index'))

        self.ll.do_long_order_change(task_to_move_id, target_id, current_user)

        return self.redirect(request.args.get('next') or self.url_for('index'))

    def task_add_tag(self, request, current_user, task_id):

        value = self.get_form_or_arg(request, 'value')
        if value is None or value == '':
            return (self.redirect(
                request.args.get('next')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_add_tag_to_task_by_id(task_id, value, current_user)

        return (self.redirect(
            request.args.get('next') or self.url_for('view_task', id=task_id)))

    def task_delete_tag(self, request, current_user, task_id, tag_id):

        if tag_id is None:
            tag_id = self.get_form_or_arg(request, 'tag_id')

        self.ll.do_delete_tag_from_task(task_id, tag_id, current_user)

        return (self.redirect(
            request.args.get('next') or self.url_for('view_task', id=task_id)))

    def task_authorize_user(self, request, current_user, task_id):

        email = self.get_form_or_arg(request, 'email')
        if email is None or email == '':
            return (self.redirect(
                request.args.get('next')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_authorize_user_for_task_by_email(task_id, email,
                                                    current_user)

        return (self.redirect(
            request.args.get('next') or self.url_for('view_task', id=task_id)))

    def task_pick_user(self, request, current_user, task_id):
        task = self.ll.get_task(task_id, current_user)
        users = self.ll.get_users()
        return self.render_template('pick_user.t.html',
                                    task=task,
                                    users=users,
                                    cycle=itertools.cycle)

    def task_authorize_user_user(self, request, current_user, task_id,
                                 user_id):
        if user_id is None or user_id == '':
            return (self.redirect(
                request.args.get('next')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_authorize_user_for_task_by_id(task_id, user_id,
                                                 current_user)

        return (self.redirect(
            request.args.get('next') or self.url_for('view_task', id=task_id)))

    def task_deauthorize_user(self, request, current_user, task_id, user_id):
        if user_id is None:
            user_id = self.get_form_or_arg(request, 'user_id')

        self.ll.do_deauthorize_user_for_task(task_id, user_id, current_user)

        return (self.redirect(
            request.args.get('next') or self.url_for('view_task', id=task_id)))

    def login(self, request, current_user):
        if request.method == 'GET':
            return self.render_template('login.t.html')
        email = request.form['email']
        password = request.form['password']
        user = self.ll.pl_get_user_by_email(email)

        if user is None:
            self.flash('Username or Password is invalid', 'error')
            return self.redirect(self.url_for('login'))
        if user.hashed_password is None or user.hashed_password == '':
            self.flash('Username or Password is invalid', 'error')
            return self.redirect(self.url_for('login'))
        if not self.check_password_hash(user.hashed_password, password):
            self.flash('Username or Password is invalid', 'error')
            return self.redirect(self.url_for('login'))

        self.login_user(user)
        self.flash('Logged in successfully')
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def logout(self, request, current_user):
        self.logout_user()
        return self.redirect(self.url_for('index'))

    def users(self, request, current_user):
        if request.method == 'GET':
            users = self.ll.get_users()
            return self.render_template('list_users.t.html',
                                        users=users,
                                        cycle=itertools.cycle)

        email = request.form['email']
        is_admin = False
        if 'is_admin' in request.form:
            is_admin = bool_from_str(request.form['is_admin'])

        self.ll.do_add_new_user(email, is_admin)

        return self.redirect(self.url_for('list_users'))

    def users_user_get(self, request, current_user, user_id):
        user = self.ll.do_get_user_data(user_id, current_user)
        return self.render_template('view_user.t.html', user=user)

    def show_hide_deleted(self, request, current_user):
        show_deleted = request.args.get('show_deleted')
        resp = self.make_response(
            self.redirect(request.args.get('next') or self.url_for('index')))
        if show_deleted and show_deleted != '0':
            resp.set_cookie('show_deleted', '1')
        else:
            resp.set_cookie('show_deleted', '')
        return resp

    def show_hide_done(self, request, current_user):
        show_done = request.args.get('show_done')
        resp = self.make_response(
            self.redirect(request.args.get('next') or self.url_for('index')))
        if show_done and show_done != '0':
            resp.set_cookie('show_done', '1')
        else:
            resp.set_cookie('show_done', '')
        return resp

    def options(self, request, current_user):
        if request.method == 'GET' or 'key' not in request.form:
            data = self.ll.get_view_options_data()
            return self.render_template('options.t.html', options=data)

        key = request.form['key']
        value = ''
        if 'value' in request.form:
            value = request.form['value']

        self.ll.do_set_option(key, value)

        return self.redirect(
            request.args.get('next') or self.url_for('view_options'))

    def option_delete(self, request, current_user, key):
        self.ll.do_delete_option(key)
        return self.redirect(
            request.args.get('next') or self.url_for('view_options'))

    def reset_order_nums(self, request, current_user):
        self.ll.do_reset_order_nums(current_user)
        return self.redirect(request.args.get('next') or self.url_for('index'))

    def export(self, request, current_user):
        if request.method == 'GET':
            return self.render_template('export.t.html', results=None)
        types_to_export = set(
            k for k in request.form.keys()
            if k in request.form and request.form[k] == 'all')
        results = self.ll.do_export_data(types_to_export)
        return jsonify(results)

    def import_(self, request, current_user):
        if request.method == 'GET':
            return self.render_template('import.t.html')

        f = request.files.get('file')
        if f is None or not f:
            r = request.form['raw']
            src = json.loads(r)
        else:
            src = json.load(f)

        self.ll.do_import_data(src)

        return self.redirect(self.url_for('index'))

    def task_crud(self, request, current_user):
        if request.method == 'GET':
            tasks = self.ll.get_task_crud_data(current_user)
            return self.render_template('task_crud.t.html',
                                        tasks=tasks,
                                        cycle=itertools.cycle)

        crud_data = {}
        for key in request.form.keys():
            if re.match(
                    r'task_\d+_(summary|deadline|is_done|is_deleted|'
                    r'order_num|duration|cost|parent_id)', key):
                crud_data[key] = request.form[key]

        self.ll.do_submit_task_crud(crud_data, current_user)

        return self.redirect(self.url_for('task_crud'))

    def tags(self, request, current_user):
        tags = self.ll.get_tags()
        return self.render_template('list_tags.t.html',
                                    tags=tags,
                                    cycle=itertools.cycle)

    def tags_id_get(self, request, current_user, tag_id):
        data = self.ll.get_tag_data(tag_id, current_user)
        return self.render_template('tag.t.html',
                                    tag=data['tag'],
                                    tasks=data['tasks'],
                                    cycle=itertools.cycle)

    def tags_id_edit(self, request, current_user, tag_id):
        def render_get_response():
            tag = self.ll.get_tag(tag_id)
            return self.render_template("edit_tag.t.html", tag=tag)

        if request.method == 'GET':
            return render_get_response()

        if 'value' not in request.form or 'description' not in request.form:
            return render_get_response()
        value = request.form['value']
        description = request.form['description']
        self.ll.do_edit_tag(tag_id, value, description)

        return self.redirect(self.url_for('view_tag', id=tag_id))

    def task_id_convert_to_tag(self, request, current_user, task_id):
        are_you_sure = request.args.get('are_you_sure')
        if are_you_sure:

            tag = self.ll.convert_task_to_tag(task_id, current_user)

            return self.redirect(
                request.args.get('next')
                or self.url_for('view_tag', id=tag.id))

        task = self.ll.get_task(task_id, current_user)
        return self.render_template('convert_task_to_tag.t.html',
                                    task_id=task.id,
                                    tag_value=task.summary,
                                    tag_description=task.description,
                                    cycle=itertools.cycle,
                                    tasks=task.children)

    def search(self, request, current_user, search_query):
        if search_query is None and request.method == 'POST':
            search_query = request.form['query']

        results = self.ll.search(search_query, current_user)

        return self.render_template('search.t.html',
                                    query=search_query,
                                    results=results)

    def task_id_add_dependee(self, request, current_user, task_id,
                             dependee_id):
        if dependee_id is None or dependee_id == '':
            dependee_id = self.get_form_or_arg(request, 'dependee_id')
        if dependee_id is None or dependee_id == '':
            return (self.redirect(
                request.args.get('next') or request.args.get('next_url')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_add_dependee_to_task(task_id, dependee_id, current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_remove_dependee(self, request, current_user, task_id,
                                dependee_id):
        if dependee_id is None:
            dependee_id = self.get_form_or_arg(request, 'dependee_id')

        self.ll.do_remove_dependee_from_task(task_id, dependee_id,
                                             current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_add_dependant(self, request, current_user, task_id,
                              dependant_id):
        if dependant_id is None or dependant_id == '':
            dependant_id = self.get_form_or_arg(request, 'dependant_id')
        if dependant_id is None or dependant_id == '':
            return (self.redirect(
                request.args.get('next') or request.args.get('next_url')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_add_dependant_to_task(task_id, dependant_id, current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_remove_dependant(self, request, current_user, task_id,
                                 dependant_id):
        if dependant_id is None:
            dependant_id = self.get_form_or_arg(request, 'dependant_id')

        self.ll.do_remove_dependant_from_task(task_id, dependant_id,
                                              current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_add_prioritize_before(self, request, current_user, task_id,
                                      prioritize_before_id):
        if prioritize_before_id is None or prioritize_before_id == '':
            prioritize_before_id = self.get_form_or_arg(
                request, 'prioritize_before_id')
        if prioritize_before_id is None or prioritize_before_id == '':
            return (self.redirect(
                request.args.get('next') or request.args.get('next_url')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_add_prioritize_before_to_task(task_id, prioritize_before_id,
                                                 current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_remove_prioritize_before(self, request, current_user, task_id,
                                         prioritize_before_id):
        if prioritize_before_id is None:
            prioritize_before_id = self.get_form_or_arg(
                request, 'prioritize_before_id')

        self.ll.do_remove_prioritize_before_from_task(task_id,
                                                      prioritize_before_id,
                                                      current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_add_prioritize_after(self, request, current_user, task_id,
                                     prioritize_after_id):
        if prioritize_after_id is None or prioritize_after_id == '':
            prioritize_after_id = self.get_form_or_arg(request,
                                                       'prioritize_after_id')
        if prioritize_after_id is None or prioritize_after_id == '':
            return (self.redirect(
                request.args.get('next') or request.args.get('next_url')
                or self.url_for('view_task', id=task_id)))

        self.ll.do_add_prioritize_after_to_task(task_id, prioritize_after_id,
                                                current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))

    def task_id_remove_prioritize_after(self, request, current_user, task_id,
                                        prioritize_after_id):
        if prioritize_after_id is None:
            prioritize_after_id = self.get_form_or_arg(request,
                                                       'prioritize_after_id')

        self.ll.do_remove_prioritize_after_from_task(task_id,
                                                     prioritize_after_id,
                                                     current_user)

        return (self.redirect(
            request.args.get('next') or request.args.get('next_url')
            or self.url_for('view_task', id=task_id)))
Пример #20
0
class Attachment(Changeable, AttachmentBase):
    _logger = logging_util.get_logger_by_name(__name__, 'Attachment')

    _id = None
    _timestamp = None
    _path = None
    _filename = None
    _description = ''

    _task = None

    def __init__(self, path, description=None, timestamp=None, filename=None,
                 lazy=None):
        super(Attachment, self).__init__(path, description, timestamp,
                                         filename)
        self._logger.debug('Attachment.__init__ %s', self)

        if lazy is None:
            lazy = {}

        self._task_lazy = lazy.get('task')

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, value):
        if value != self._id:
            self._on_attr_changing(self.FIELD_ID, self._id)
            self._id = value
            self._on_attr_changed(self.FIELD_ID, self.OP_SET, self._id)

    @property
    def timestamp(self):
        return self._timestamp

    @timestamp.setter
    def timestamp(self, value):
        if value != self._timestamp:
            self._on_attr_changing(self.FIELD_TIMESTAMP, self._timestamp)
            self._timestamp = value
            self._on_attr_changed(self.FIELD_TIMESTAMP, self.OP_SET,
                                  self._timestamp)

    @property
    def path(self):
        return self._path

    @path.setter
    def path(self, value):
        if value != self._path:
            self._on_attr_changing(self.FIELD_PATH, self._path)
            self._path = value
            self._on_attr_changed(self.FIELD_PATH, self.OP_SET, self._path)

    @property
    def filename(self):
        return self._filename

    @filename.setter
    def filename(self, value):
        if value != self._filename:
            self._on_attr_changing(self.FIELD_FILENAME, self._filename)
            self._filename = value
            self._on_attr_changed(self.FIELD_FILENAME, self.OP_SET,
                                  self._filename)

    @property
    def description(self):
        return self._description

    @description.setter
    def description(self, value):
        if value != self._description:
            self._on_attr_changing(self.FIELD_DESCRIPTION, self._description)
            self._description = value
            self._on_attr_changed(self.FIELD_DESCRIPTION, self.OP_SET,
                                  self._description)

    @property
    def task_id(self):
        if self.task:
            return self.task.id
        return None

    def _populate_task(self):
        if self._task_lazy:
            self._logger.debug('populating task from lazy %s', self)
            value = self._task_lazy()
            self._task_lazy = None
            self.task = value

    @property
    def task(self):
        self._populate_task()
        return self._task

    @task.setter
    def task(self, value):
        self._populate_task()
        if value != self._task:
            self._on_attr_changing(self.FIELD_TASK, self._task)
            if self._task is not None:
                self._task.attachments.discard(self)
            self._task = value
            if self._task is not None:
                self._task.attachments.add(self)
            self._on_attr_changed(self.FIELD_TASK, self.OP_SET, self._task)

    def clear_relationships(self):
        self.task = None
Пример #21
0
class Task(Changeable, TaskBase):
    _logger = logging_util.get_logger_by_name(__name__, 'Task')

    _id = None
    _summary = None
    _description = None
    _is_done = None
    _is_deleted = None
    _order_num = None
    _deadline = None
    _expected_duration_minutes = None
    _expected_cost = None
    _parent = None
    _is_public = None

    def __init__(self,
                 summary,
                 description='',
                 is_done=False,
                 is_deleted=False,
                 deadline=None,
                 expected_duration_minutes=None,
                 expected_cost=None,
                 is_public=False,
                 lazy=None):
        super(Task, self).__init__(summary, description, is_done, is_deleted,
                                   deadline, expected_duration_minutes,
                                   expected_cost, is_public)

        self._logger.debug('Task.__init__ %s', self)

        if lazy is None:
            lazy = {}

        self._parent_lazy = lazy.get('parent')

        # self depends on self.dependees
        self._dependees = InterlinkedDependees(self,
                                               lazy=lazy.get('dependees'))
        # self.dependants depend on self
        self._dependants = InterlinkedDependants(self,
                                                 lazy=lazy.get('dependants'))
        # self is after self.prioritize_before's
        # self has lower priority than self.prioritize_before's
        self._prioritize_before = InterlinkedPrioritizeBefore(
            self, lazy=lazy.get('prioritize_before'))
        # self is before self.prioritize_after's
        # self has higher priority than self.prioritize_after's
        self._prioritize_after = InterlinkedPrioritizeAfter(
            self, lazy=lazy.get('prioritize_after'))
        self._tags = InterlinkedTags(self, lazy=lazy.get('tags'))
        self._users = InterlinkedUsers(self, lazy=lazy.get('users'))
        self._children = InterlinkedChildren(self, lazy=lazy.get('children'))
        self._notes = InterlinkedNotes(self, lazy=lazy.get('notes'))
        self._attachments = InterlinkedAttachments(
            self, lazy=lazy.get('attachments'))

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, value):
        if value != self._id:
            self._logger.debug('%s: %s -> %s', self, self.id, value)
            self._on_attr_changing(self.FIELD_ID, self._id)
            self._id = value
            self._on_attr_changed(self.FIELD_ID, self.OP_SET, self._id)

    @property
    def summary(self):
        return self._summary

    @summary.setter
    def summary(self, value):
        if value != self._summary:
            self._logger.debug('%s: %s -> %s', self, repr(self.summary),
                               repr(value))
            self._on_attr_changing(self.FIELD_SUMMARY, self._summary)
            self._summary = value
            self._on_attr_changed(self.FIELD_SUMMARY, self.OP_SET,
                                  self._summary)

    @property
    def description(self):
        return self._description

    @description.setter
    def description(self, value):
        if value != self._description:
            self._logger.debug('%s: %s -> %s', self, self.description, value)
            self._on_attr_changing(self.FIELD_DESCRIPTION, self._description)
            self._description = value
            self._on_attr_changed(self.FIELD_DESCRIPTION, self.OP_SET,
                                  self._description)

    @property
    def is_done(self):
        return self._is_done

    @is_done.setter
    def is_done(self, value):
        if value != self._is_done:
            self._logger.debug('%s: %s -> %s', self, self.is_done, value)
            self._on_attr_changing(self.FIELD_IS_DONE, self._is_done)
            self._is_done = value
            self._on_attr_changed(self.FIELD_IS_DONE, self.OP_SET,
                                  self._is_done)

    @property
    def is_deleted(self):
        return self._is_deleted

    @is_deleted.setter
    def is_deleted(self, value):
        if value != self._is_deleted:
            self._logger.debug('%s: %s -> %s', self, self.is_deleted, value)
            self._on_attr_changing(self.FIELD_IS_DELETED, self._is_deleted)
            self._is_deleted = value
            self._on_attr_changed(self.FIELD_IS_DELETED, self.OP_SET,
                                  self._is_deleted)

    @property
    def order_num(self):
        return self._order_num

    @order_num.setter
    def order_num(self, value):
        if value != self._order_num:
            self._logger.debug('%s: %s -> %s', self, self.order_num, value)
            self._on_attr_changing(self.FIELD_ORDER_NUM, self._order_num)
            self._order_num = value
            self._on_attr_changed(self.FIELD_ORDER_NUM, self.OP_SET,
                                  self._order_num)

    @property
    def deadline(self):
        return self._deadline

    @deadline.setter
    def deadline(self, value):
        if value != self._deadline:
            self._logger.debug('%s: %s -> %s', self, self.deadline, value)
            self._on_attr_changing(self.FIELD_DEADLINE, self._deadline)
            self._deadline = value
            self._on_attr_changed(self.FIELD_DEADLINE, self.OP_SET,
                                  self._deadline)

    @property
    def expected_duration_minutes(self):
        return self._expected_duration_minutes

    @expected_duration_minutes.setter
    def expected_duration_minutes(self, value):
        if value != self._expected_duration_minutes:
            self._logger.debug('%s: %s -> %s', self,
                               self.expected_duration_minutes, value)
            self._on_attr_changing(self.FIELD_EXPECTED_DURATION_MINUTES,
                                   self._expected_duration_minutes)
            self._expected_duration_minutes = value
            self._on_attr_changed(self.FIELD_EXPECTED_DURATION_MINUTES,
                                  self.OP_SET, self._expected_duration_minutes)

    @property
    def expected_cost(self):
        return self._expected_cost

    @expected_cost.setter
    def expected_cost(self, value):
        if value != self._expected_cost:
            self._logger.debug('%s: %s -> %s', self, self.expected_cost, value)
            self._on_attr_changing(self.FIELD_EXPECTED_COST,
                                   self._expected_cost)
            self._expected_cost = value
            self._on_attr_changed(self.FIELD_EXPECTED_COST, self.OP_SET,
                                  self._expected_cost)

    @property
    def parent_id(self):
        if self.parent:
            return self.parent.id
        return None

    def _populate_parent(self):
        if self._parent_lazy:
            self._logger.debug('populating parent from lazy %s', self)
            value = self._parent_lazy()
            self._parent_lazy = None
            self.parent = value

    @property
    def parent(self):
        self._populate_parent()
        return self._parent

    @parent.setter
    def parent(self, value):
        self._logger.debug('%s', self)
        self._populate_parent()
        if value != self._parent:
            self._logger.debug('%s: %s -> %s', self, self._parent, value)
            self._on_attr_changing(self.FIELD_PARENT, self._parent)
            if self._parent is not None:
                self._parent.children.discard(self)
            self._parent = value
            if self._parent is not None:
                self._parent.children.append(self)
            self._on_attr_changed(self.FIELD_PARENT, self.OP_SET, self._parent)

    @property
    def children(self):
        return self._children

    @property
    def tags(self):
        return self._tags

    @property
    def users(self):
        return self._users

    @property
    def dependees(self):
        return self._dependees

    @property
    def dependants(self):
        return self._dependants

    @property
    def prioritize_before(self):
        return self._prioritize_before

    @property
    def prioritize_after(self):
        return self._prioritize_after

    @property
    def notes(self):
        return self._notes

    @property
    def attachments(self):
        return self._attachments

    @property
    def is_public(self):
        return self._is_public

    @is_public.setter
    def is_public(self, value):
        if value != self._is_public:
            self._logger.debug('%s: %s -> %s', self, self.is_public, value)
            self._on_attr_changing(self.FIELD_IS_PUBLIC, self._is_public)
            self._is_public = value
            self._on_attr_changed(self.FIELD_IS_PUBLIC, self.OP_SET,
                                  self._is_public)

    def contains_dependency_cycle(self, visited=None):
        self._logger.debug('%s', self)
        if visited is None:
            visited = set()
        if self in visited:
            return True
        visited = set(visited)
        visited.add(self)
        for dependee in self.dependees:
            if dependee.contains_dependency_cycle(visited):
                return True

        return False

    def contains_priority_cycle(self, visited=None):
        self._logger.debug('%s', self)
        if visited is None:
            visited = set()
        if self in visited:
            return True
        visited = set(visited)
        visited.add(self)
        for before in self.prioritize_before:
            if before.contains_priority_cycle(visited):
                return True
        return False

    def clear_relationships(self):
        self._logger.debug('%s', self)
        self.parent = None
        self.children.clear()
        self.tags.clear()
        self.users.clear()
        self.notes.clear()
        self.attachments.clear()
        self.dependees.clear()
        self.dependants.clear()
        self.prioritize_before.clear()
        self.prioritize_after.clear()
Пример #22
0
class InterlinkedSet(collections.MutableSet):
    _logger = logging_util.get_logger_by_name(__name__, 'InterlinkedSet')

    __change_field__ = None
    __attr_counterpart__ = None

    def __init__(self, container, lazy=None):
        self._logger.debug('__init__')
        if container is None:
            raise ValueError('container cannot be None')

        self.container = container
        self.set = set()
        self._lazy = lazy

    def __repr__(self):
        self._logger.debug('__repr__')
        cls = type(self).__name__
        if self._lazy:
            return '{}(<lazy>)'.format(cls)
        return '{}({})'.format(cls, self.set)

    def _populate(self):
        self._logger.debug('_populate')
        if self._lazy:
            self._logger.debug('populating the collection')
            self.set.update(self._lazy)
            self._lazy = None

    @property
    def c(self):
        return self.container

    def __len__(self):
        self._logger.debug('__len__')
        self._populate()
        return len(self.set)

    def __contains__(self, item):
        self._logger.debug('__contains__')
        self._populate()
        return self.set.__contains__(item)

    def __iter__(self):
        self._logger.debug('__iter__')
        self._populate()
        return self.set.__iter__()

    def append(self, item):
        self._logger.debug('append')
        self._logger.debug('%s: %s', self.c, item)
        self._populate()
        self.add(item)

    def __str__(self):
        self._logger.debug('__str__')
        if self._lazy:
            return 'set(<lazy>)'
        return str(self.set)

    def _add(self, item):
        self._logger.debug('_add')
        self._populate()
        self.set.add(item)

    def _discard(self, item):
        self._logger.debug('_discard')
        self._populate()
        self.set.discard(item)

    def count(self):
        return len(self)
Пример #23
0
class InterlinkedUsers(ManyToManySet):
    __change_field__ = TaskBase.FIELD_USERS
    __attr_counterpart__ = 'tasks'
    _logger = logging_util.get_logger_by_name(__name__, 'InterlinkedUsers')
Пример #24
0
class Tag(Changeable, TagBase):
    _logger = logging_util.get_logger_by_name(__name__, 'Tag')

    _id = None
    _value = None
    _description = None

    _tasks = None

    def __init__(self, value, description=None, lazy=None):
        super(Tag, self).__init__(value=value, description=description)
        self._logger.debug('Tag.__init__ %s', self)

        if lazy is None:
            lazy = {}

        self._tasks = InterlinkedTasks(self, lazy=lazy.get('tasks'))

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, value):
        if value != self._id:
            self._logger.debug('%s: %s -> %s', self, self._id, value)
            self._on_attr_changing(self.FIELD_ID, self._id)
            self._id = value
            self._on_attr_changed(self.FIELD_ID, self.OP_SET, self._id)

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

    @value.setter
    def value(self, value):
        if value != self._value:
            self._logger.debug('%s: %s -> %s', self, self._value, value)
            self._on_attr_changing(self.FIELD_VALUE, self._value)
            self._value = value
            self._on_attr_changed(self.FIELD_VALUE, self.OP_SET, self._value)

    @property
    def description(self):
        return self._description

    @description.setter
    def description(self, value):
        if value != self._description:
            self._logger.debug('%s: %s -> %s', self, self._description, value)
            self._on_attr_changing(self.FIELD_DESCRIPTION, self._description)
            self._description = value
            self._on_attr_changed(self.FIELD_DESCRIPTION, self.OP_SET,
                                  self._description)

    @property
    def tasks(self):
        return self._tasks

    def clear_relationships(self):
        self.tasks.clear()
Пример #25
0
class InterlinkedNotes(OneToManySet):
    __change_field__ = TaskBase.FIELD_NOTES
    __attr_counterpart__ = 'task'
    _logger = logging_util.get_logger_by_name(__name__, 'InterlinkedNotes')