def tasks(self, refresh=False, include_deleted=False, include_done=False, include_checked=True, include_unchecked=True): """Return a remote or chached task list for user.""" if not self.tasks_list or refresh: params = { 'includeDeleted': str(include_deleted).lower(), 'includeDone': str(include_done).lower(), } tasks_data = request.get(url=CONSTANTS.get('TASKS_URL'), session=self.session(), params=params) self.tasks_list = [ Task(data_dict=task, user=self) for task in tasks_data ] return Task.filter_tasks(self.tasks_list, include_deleted=include_deleted, include_done=include_done, include_checked=include_checked, include_unchecked=include_unchecked)
def tasks(self, refresh=False, include_deleted=False, include_done=False, include_checked=True, include_unchecked=True): """Return a remote or chached task list for user.""" if not self.tasks_list or refresh: params = { 'includeDeleted': str(include_deleted).lower(), 'includeDone': str(include_done).lower(), } tasks_data = request.get( url=CONSTANTS.get('TASKS_URL'), session=self.session(), params=params ) self.tasks_list = [Task(data_dict=task, user=self) for task in tasks_data] return Task.filter_tasks(self.tasks_list, include_deleted=include_deleted, include_done=include_done, include_checked=include_checked, include_unchecked=include_unchecked)
def get_user(self, refresh=False): """Return a user object currently logged in.""" if not self.user or refresh: data = request.get(url=CONSTANTS.get('ME_URL'), session=self.session) data.update({'password': self.password}) self.password = None self.user = User(data_dict=data, session=self.session) return self.user
def get_user(self, refresh=False): """Return a user object currently logged in.""" if not self.user or refresh: data = request.get( url=CONSTANTS.get('ME_URL'), session=self.session ) data.update({'password': self.password}) self.password = None self.user = User(data_dict=data, session=self.session) return self.user
def create_user(cls, **fields): """Create new user by required parameters.""" User.check_for_missed_fields(fields) json_data = { 'name': fields.get('name'), 'username': fields.get('email'), 'password': fields.get('password'), 'emails': fields.get('emails', fields.get('email')), 'phoneNumbers': fields.get('phone_numbers', []) } request.post(url=CONSTANTS.get('USER_URL'), json=json_data) user = Client(email=fields.get('email'), password=fields.get('password')).get_user() return user
def create_user(cls, **fields): """Create new user by required parameters.""" User.check_for_missed_fields(fields) json_data = { 'name': fields.get('name'), 'username': fields.get('email'), 'password': fields.get('password'), 'emails': fields.get('emails', fields.get('email')), 'phoneNumbers': fields.get('phone_numbers', []) } request.post( url=CONSTANTS.get('USER_URL'), json=json_data ) user = Client(email=fields.get('email'), password=fields.get('password')).get_user() return user
def categories(self, refresh=False, include_deleted=False): """Return a remote or cached categories list for user.""" if not self.categories_list or refresh: params = { 'includeDeleted': str(include_deleted).lower(), } categories_data = request.get(url=CONSTANTS.get('CATEGORIES_URL'), session=self.session(), params=params) self.categories_list = [ Category(data_dict=category, user=self) for category in categories_data ] result = self.categories_list if not include_deleted: result = [cat for cat in result if not cat['isDeleted']] return result
def __log_in(self, email, password): """ Authentication base on `email` and `password`. Return an actual session, used internally for all following requests to API. """ credentials = { 'j_username': email, 'j_password': password, '_spring_security_remember_me': 'on' } headers = {'Content-Type': 'application/x-www-form-urlencoded'} self.session = requests.Session() request.post(url=CONSTANTS.get('LOGIN_URL'), session=self.session, headers=headers, data=credentials, response_json=False) return self.session
def categories(self, refresh=False, include_deleted=False): """Return a remote or cached categories list for user.""" if not self.categories_list or refresh: params = { 'includeDeleted': str(include_deleted).lower(), } categories_data = request.get( url=CONSTANTS.get('CATEGORIES_URL'), session=self.session(), params=params ) self.categories_list = [ Category(data_dict=category, user=self) for category in categories_data ] result = self.categories_list if not include_deleted: result = [cat for cat in result if not cat['isDeleted']] return result
def __log_in(self, email, password): """ Authentication base on `email` and `password`. Return an actual session, used internally for all following requests to API. """ credentials = { 'j_username': email, 'j_password': password, '_spring_security_remember_me': 'on' } headers = {'Content-Type': 'application/x-www-form-urlencoded'} self.session = requests.Session() request.post( url=CONSTANTS.get('LOGIN_URL'), session=self.session, headers=headers, data=credentials, response_json=False ) return self.session
class User(Resource): """ `User` is the class representing User object. It wraps user-related JSON into class instances and responsible for user management. """ _endpoint = CONSTANTS.get('ME_URL') _reserved_attrs = ('data_dict', 'session_obj', 'is_dirty') __alternate_endpoint = CONSTANTS.get('USER_URL') def __init__(self, data_dict, session): """Constructor for User.""" super(User, self).__init__(data_dict) self.session_obj = session self.categories_list = None self.tasks_list = None self._pending_tasks = None def save(self, alternate_endpoint=None): """ Pushe updated attributes to the server. If nothing was changed we dont hit the API. """ super(User, self).save(alternate_endpoint=self.get_endpoint()) def session(self): """Shortcut to retrive object session for requests.""" return self.session_obj def destroy(self, alternate_endpoint=None): """ Hit the API to destroy the user. Pass a changed alternate endpoint as it is differ from the class one. """ super(User, self).destroy(alternate_endpoint=self.__alternate_endpoint) delete = destroy def refresh(self, alternate_endpoint=None): """ Reload resource data from remote API. Pass a changed alternate endpoint as it is differ from the class one. """ super(User, self).refresh(alternate_endpoint=self.get_endpoint()) # pylint: disable=too-many-arguments def tasks(self, refresh=False, include_deleted=False, include_done=False, include_checked=True, include_unchecked=True): """Return a remote or chached task list for user.""" if not self.tasks_list or refresh: params = { 'includeDeleted': str(include_deleted).lower(), 'includeDone': str(include_done).lower(), } tasks_data = request.get(url=CONSTANTS.get('TASKS_URL'), session=self.session(), params=params) self.tasks_list = [ Task(data_dict=task, user=self) for task in tasks_data ] return Task.filter_tasks(self.tasks_list, include_deleted=include_deleted, include_done=include_done, include_checked=include_checked, include_unchecked=include_unchecked) def categories(self, refresh=False, include_deleted=False): """Return a remote or cached categories list for user.""" if not self.categories_list or refresh: params = { 'includeDeleted': str(include_deleted).lower(), } categories_data = request.get(url=CONSTANTS.get('CATEGORIES_URL'), session=self.session(), params=params) self.categories_list = [ Category(data_dict=category, user=self) for category in categories_data ] result = self.categories_list if not include_deleted: result = [cat for cat in result if not cat['isDeleted']] return result def add_task(self, task): """Add new task into internal storage.""" if self.tasks_list: self.tasks_list.append(task) else: self.tasks_list = [task] def add_category(self, category): """Add new category into internal storage.""" if self.categories_list: self.categories_list.append(category) else: self.categories_list = [category] def default_category(self): """Return default category for user if exist.""" return next((cat for cat in self.categories() if cat.isDefault), None) def pending_tasks(self, refresh=False): """ Return a list of dicts representing a pending task that was shared with current user. Empty list otherwise. """ if not self._pending_tasks or refresh: response_obj = request.get(url=self.get_endpoint() + '/pending', session=self.session()) self._pending_tasks = response_obj['pendingTasks'] return self._pending_tasks or [] def pending_tasks_ids(self, refresh=False): """ Return a list of pending tasks ids shared with user. Empty list otherwise. """ return [task['id'] for task in self.pending_tasks(refresh=refresh)] def approve_pending_task(self, pending_task_id=None, pending_task=None): """ Approve pending task via API call. Accept pending_task_id or pending_task dict (in format of pending_tasks. """ task_id = pending_task_id or pending_task['id'] if not task_id: raise errors.ModelAttributeError( 'Eather :pending_task_id or :pending_task argument is required.' ) response_obj = request.post(url=self.get_endpoint() + '/pending/' + task_id + '/accept', session=self.session()) return response_obj @staticmethod def required_attributes(): """ Return a set of required fields for valid user creation. This tuple is checked to prevent unnecessary API calls. """ return {'name', 'email', 'password'}
class Task(Resource): """ `Task` is the class representing user task object. It wraps task-related JSON into class instances and responsible for task management. """ _endpoint = CONSTANTS.get('TASKS_URL') _reserved_attrs = ('user', 'data_dict', 'is_dirty') def __init__(self, data_dict, user): """Constructor for Task.""" super(Task, self).__init__(data_dict) self.user = user def check(self): """ Mark task as CHECKED. Using update functionality under the hood. """ self['status'] = 'CHECKED' self.is_dirty = True self.save() def done(self): """ Mark task as DONE. Using update functionality under the hood. """ self['status'] = 'DONE' self.save() def session(self): """Shortcut to retrive user session for requests.""" return self.user.session() def subtasks(self): """Return a list with subtasks of current task for same user.""" return [task for task in self.user.tasks() if task['parentGlobalTaskId'] == self['id']] def create_subtask(self, **fields): """Create a new tasks from provided fields and makes it an subtask of current one.""" subtask_attrs = fields.copy() subtask_attrs.update({'parentGlobalTaskId': self['id']}) subtask = Task.create(user=self.user, **subtask_attrs) self.user.add_task(subtask) return subtask def add_subtask(self, subtask): """ Add subtask to current task. Change synhronized remotly. """ # Currently we don't support task reassignment from one user to another subtask['parentGlobalTaskId'] = self['id'] subtask.save() def notes(self): """Return a parsed list of notes for the task.""" if self.note: return [note for note in self.note.split('\n') if note] return [] def add_note(self, text_note): """Add a text note to current task. Change synhronized remotly. """ note = self['note'] or '' if not note.endswith('\n'): note = note + '\n' self['note'] = note + text_note self.save() def members(self): """ Return a list of dicts {username:email} representing the task members, including owner. If the task is not share returns only owner info. """ objects_list = self['sharedMembers'] if objects_list: return [{member['target']: member['name']} for member in objects_list] return [{self.user['email']: self.user['name']}] def share_with(self, new_member, message=None): """ Share a task with new member. Pushes changes to server. Updates task members list and new member tasks list. """ json_data = { 'invitees': [{'email': new_member['email']}], 'message': message } response_obj = request.post( url=self.get_endpoint() + '/' + self['id'] + '/share', json=json_data, session=self.session() ) self.data_dict = response_obj return self def category(self): """Return a category object based mapped to selected task.""" return next( (cat for cat in self.user.categories() if cat['id'] == self['categoryId']), None ) def parent(self): """Return parent task object for subtask and None for first-level task.""" return next( (task for task in self.user.tasks() if task['id'] == self['parentGlobalTaskId']), None ) @staticmethod def required_attributes(): """ Return a set of required fields for valid task creation. This tuple is checked to prevent unnecessary API calls. Seems that :id and :title attributes are required for now, where :id is set automatically. """ return {'title'} @staticmethod def filter_tasks(tasks_list, **filters): """ Filter tasks by their status. Returns a new filtered list. """ result = tasks_list[:] statuses = list(TASK_STATUSES) if not filters.get('include_deleted', False): statuses.remove('DELETED') if not filters.get('include_done', False): statuses.remove('DONE') if not filters.get('include_checked', False): statuses.remove('CHECKED') if not filters.get('include_unchecked', False): statuses.remove('UNCHECKED') result = [task for task in result if task['status'] in statuses] return list(result) @classmethod def _create_callback(cls, resource_json, user): """ Callback method that is called automaticly after each successfull creation via remote API. Return an task instance. """ task = cls(data_dict=resource_json[0], user=user) user.add_task(task) return task
class Category(Resource): """ `Category` is the class representing user category object. It wraps task-related JSON into class instances and responsible for categories management. """ _reserved_attrs = ('user', 'data_dict', 'is_dirty') _endpoint = CONSTANTS.get('CATEGORIES_URL') def __init__(self, data_dict, user): """Constructor for Category.""" super(Category, self).__init__(data_dict) self.user = user def session(self): """Shortcut to retrive user session for requests.""" return self.user.session() def mark_default(self): """ Shortcut to mark a category as default one locally. Mark previous default one as not default. """ previous = self.user.default_category() previous.default = False previous.isDefault = False previous.save() self['default'] = True self['isDefault'] = True self.save() return self def tasks(self): """Return a list of the user tasks that belongs to selected category.""" tasks = self.user.tasks() return [task for task in tasks if task.categoryId == self['id']] def add_task(self, task): """ Add new task into category. Update task and pushes changes remotly. """ task.categoryId = self['id'] task.save() def remove_task(self, task): """ Remove a task from the category and move to default one. Updates task and pushes changes remotly. If category already default do nothing. """ if task.category()['isDefault']: raise errors.ModelError( 'Can not remove task from default category') task.categoryId = self.user.default_category()['id'] task.save() @staticmethod def required_attributes(): """ Return a set of required fields for valid task creation. This tuple is checked to prevent unnecessary API calls. Seems that :id and :title attributes are required for now, where :id is set automatically. """ return {'name'} @classmethod def _create_callback(cls, resource_json, user): """ Callback method that is called automaticly after each successfull creation via remote API. Return an category instance. """ category = cls(data_dict=resource_json[0], user=user) user.add_category(category) return category