From d7b919701cbfa8e90484090a57cd474584afa6a3 Mon Sep 17 00:00:00 2001 From: Aaron Gutierrez Date: Wed, 7 Mar 2018 08:46:40 -0800 Subject: [PATCH] Out with the old --- Makefile | 8 +- cmdasana.py | 288 ----------------------------------------- data.py | 89 ------------- ui.py | 365 ---------------------------------------------------- 4 files changed, 2 insertions(+), 748 deletions(-) delete mode 100755 cmdasana.py delete mode 100644 data.py delete mode 100644 ui.py diff --git a/Makefile b/Makefile index 196babc..f7dbcd2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,2 @@ -all: - git submodule init - git submodule update - -tags: ui/*.py main.py - ctags -R ui main.py +tags: ui/*.py models/*.py main.py auth.py asana_service.py + ctags -R ui models *.py diff --git a/cmdasana.py b/cmdasana.py deleted file mode 100755 index 6d14060..0000000 --- a/cmdasana.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env python -# -*- coding: latin-1 -*- -import json -import os -import sys -from threading import Thread - -import urwid -import asana - -import ui -from auth import Auth -from data import Data - -class CmdAsana: - loop = None - - def __init__(self): - auth = Auth() - self.data = Data(auth.getClient()) - - def saveState(self): - f = open('.state', 'w') - f.write(json.dumps(self.state)) - f.close() - - def loadState(self): - try: - f = open('.state', 'r') - self.state = json.loads(f.readline()) - f.close() - except IOError: - workspace_id = self.myWorkspaces()[0]['id'] - self.state = { - 'view': 'workspace', - 'id': workspace_id, - 'workspace_id': workspace_id - } - - - def newTask(self, task_after_id): - def runInThread(): - if self.state['view'] == 'project': - task = self.data.createTask( - self.state['workspace_id'], - opt_projects=[self.state['id']], - opt_after=task_after_id - ) - else: - task = self.data.createTask( - self.state['workspace_id'], - assignee=self.me['id'] - ) - - update(task) - - def update(task): - task_list,_ = self.frame.contents[1] - task_list.insertNewTask(task) - self.loop.draw_screen() - - thread = Thread(target=runInThread) - thread.start() - - def updateTask(self, task_id, opt_name=None, opt_projects=None, - opt_assignee=None, opt_notes=None): - def runInThread(): - self.data.updateTask(task_id, - opt_name=opt_name, - opt_projects=opt_projects, - opt_assignee=opt_assignee, - opt_notes=opt_notes) - - thread = Thread(target=runInThread) - thread.start() - - def addComment(self, task_id, comment): - def runInThread(): - self.data.addComment(task_id, comment) - self.showDetails(task_id, show_loading=False) - - thread = Thread(target=runInThread) - thread.start() - - def userTypeAhead(self, text, callback): - def runInThread(): - callback(self.data.userTypeahead(self.state['workspace_id'], text)) - self.loop.draw_screen() - - thread = Thread(target=runInThread) - thread.start() - - def replaceBody(self, widget): - old_widget,_ = self.frame.contents.pop() - if old_widget != None: - self.clearSignals(old_widget) - self.frame.contents.append((widget, self.frame.options())) - self.frame.focus_position = 0 - if self.loop != None: - self.loop.draw_screen() - - def showMainLoading(self): - text = urwid.Text(('loading', '[loading...]')) - self.replaceBody(urwid.Filler(text)) - - def showMyTasks(self, workspace_id): - self.state['view'] = 'atm' - self.state['id'] = workspace_id - self.state['workspace_id'] = workspace_id - - self.showMainLoading() - - def runInThread(): - tasks = self.data.myTasks(workspace_id) - update(tasks) - - def update(tasks): - task_list = ui.TaskList(tasks) - self.connectTaskListSignals(task_list) - self.replaceBody(task_list) - - thread = Thread(target=runInThread) - thread.start() - - def showProject(self, project_id): - if project_id == None: - return self.showMyTasks(self.state['workspace_id']) - self.state['view'] = 'project' - self.state['id'] = project_id - - self.showMainLoading() - - def runInThread(): - tasks = self.data.tasksInProject(project_id) - update(tasks) - - def update(tasks): - task_list = ui.TaskList(tasks) - self.connectTaskListSignals(task_list) - self.replaceBody(task_list) - - thread = Thread(target=runInThread) - thread.start() - - def showProjectList(self, workspace_id): - self.state['view'] = 'workspace' - self.state['id'] = workspace_id - self.state['workspace_id'] = workspace_id - self.workspace_id = workspace_id - - self.showMainLoading() - - def runInThread(): - projects = self.data.allMyProjects() - update(projects) - - def update(projects): - project_list = ui.ProjectList(projects) - urwid.connect_signal(project_list, 'loadproject', self.showProject) - self.replaceBody(project_list) - - thread = Thread(target=runInThread) - thread.start() - - def showDetails(self, task_id, show_loading=True): - self.state['view'] = 'details' - self.state['id'] = task_id - - if show_loading: - self.showMainLoading() - - def runInThread(): - task, stories, subtasks = self.data.getTask(task_id) - update(task, stories, subtasks) - - def update(task, stories, subtasks): - task_details = ui.TaskDetails(task, stories, subtasks) - self.connectDetailsSignals(task_details) - self.replaceBody(task_details) - - thread = Thread(target=runInThread) - thread.start() - - def registerSignals(self): - urwid.register_signal(ui.TaskList, [ - 'complete', - 'newtask', - 'updatetask', - 'details', - ]) - urwid.register_signal(ui.TaskEdit, [ - 'complete', - 'newtask', - 'updatetask', - 'details', - ]) - - urwid.register_signal(ui.TaskDetails, [ - 'comment', - 'loadproject', - 'updatedescription', - 'updatetask', - 'usertypeahead', - 'assigntask', - 'complete', - 'newtask', - 'details', - ]) - - urwid.register_signal(ui.AssigneeTypeAhead, [ - 'usertypeahead', - 'assigntask', - ]) - - urwid.register_signal(ui.CommentEdit, ['comment']) - urwid.register_signal(ui.DescriptionEdit, ['updatedescription']) - urwid.register_signal(ui.TaskNameEdit, 'updatetask') - urwid.register_signal(ui.WorkspaceMenu, 'click') - urwid.register_signal(ui.ProjectList, 'loadproject') - - - def clearSignals(self, widget): - urwid.disconnect_signal(widget, 'complete', self.completeTask) - urwid.disconnect_signal(widget, 'newtask', self.newTask) - urwid.disconnect_signal(widget, 'updatetask', self.updateTask) - urwid.disconnect_signal(widget, 'details', self.showDetails) - urwid.disconnect_signal(widget, 'updatedescription', - self.updateDescription) - urwid.disconnect_signal(widget, 'updatetask', self.updateTask) - urwid.disconnect_signal(widget, 'usertypeahead', self.userTypeAhead) - - def connectTaskListSignals(self, task_list): - urwid.connect_signal(task_list, 'complete', self.completeTask) - urwid.connect_signal(task_list, 'newtask', self.newTask) - urwid.connect_signal(task_list, 'updatetask', self.updateTask) - urwid.connect_signal(task_list, 'details', self.showDetails) - - def connectDetailsSignals(self, task_details): - urwid.connect_signal(task_details, 'comment', self.addComment) - urwid.connect_signal(task_details, 'loadproject', self.showProject) - urwid.connect_signal(task_details, 'updatedescription', - self.updateDescription) - urwid.connect_signal(task_details, 'updatetask', self.updateTask) - urwid.connect_signal(task_details, 'usertypeahead', self.userTypeAhead) - urwid.connect_signal(task_details, 'assigntask', self.assignTask) - urwid.connect_signal(task_details, 'complete', self.completeTask) - urwid.connect_signal(task_details, 'newtask', self.newTask) - urwid.connect_signal(task_details, 'details', self.showDetails) - - def handleInput(self, key): - if key in ('q', 'Q'): - raise urwid.ExitMainLoop() - - def render(self): - urwid.set_encoding("UTF-8") - self.registerSignals() - - workspace_menu = ui.WorkspaceMenu(self.data.myWorkspaces()) - urwid.connect_signal(workspace_menu, 'click', self.showProjectList) - - self.frame = urwid.Pile([ - ('pack', urwid.AttrMap(workspace_menu, 'workspace')), - None - ]) - if self.state['view'] == 'workspace': - self.showProjectList(self.state['id']) - elif self.state['view'] == 'project': - self.showProject(self.state['id']) - elif self.state['view'] == 'atm': - self.showMyTasks(self.state['id']) - elif self.state['view'] == 'details': - self.showDetails(self.state['id']) - else: - raise KeyError - - self.loop = urwid.MainLoop(self.frame, - unhandled_input=self.handleInput, - palette=ui.palette - ) - self.loop.run() - -def main(): - cmdasana = CmdAsana() - cmdasana.loadState() - cmdasana.render() - cmdasana.saveState() - -if __name__ == "__main__": main() diff --git a/data.py b/data.py deleted file mode 100644 index 5eee75e..0000000 --- a/data.py +++ /dev/null @@ -1,89 +0,0 @@ -import asana - -# id of the personal projects domain -PERSONAL = 498346170860 - -class Data: - def __init__(self, client): - self.client = client - self.me = self.client.users.me() - - def me(self): - return self.me - - def myWorkspaces(self): - return self.me['workspaces'] - - def myTasks(self, workspace_id): - return self.client.tasks.find_all(params={ - 'assignee': self.me['id'], - 'workspace': workspace_id, - 'completed_since': 'now' - }) - - def allMyProjects(self): - if self.workspace_id != PERSONAL: - return self.client.projects.find_by_workspace(self.workspace_id) - else: - return self.client.projects.find_by_workspace(self.workspace_id, - page_size=None) - - def tasksInProject(self, project_id): - return self.client.tasks.find_by_project(project_id, params={ - 'completed_since': 'now' - }) - - def getTask(self, task_id): - task = self.client.tasks.find_by_id(task_id, params={ - 'opt_fields': ['name', - 'assignee.name', - 'due_date', - 'notes', - 'parent', - 'projects.name', - 'subtasks.name' - ] - }) - - stories = self.client.stories.find_by_task(task_id) - return (task, stories, task['subtasks']) - - def completeTask(self, task_id): - self.client.tasks.update(task_id, completed=True) - - def createTask(self, workspace, opt_projects=[], opt_after=None, - opt_assignee=None, opt_name=None): - task = self.client.tasks.create_in_workspace(workspace, - projects=opt_projects, - assignee=opt_assignee, - name=opt_name) - if opt_after and len(opt_projects) > 0: - self.client.tasks.add_project(task['id'], - project=opt_projects[0], - insert_after=opt_after) - - return task - - def updateTask(self, task_id, opt_name=None, opt_projects=None, - opt_assignee=None, opt_notes=None): - return self.client.tasks.update(task_id, - name=opt_name, - projects=opt_projects, - assignee=opt_assignee, - notes=opt_notes) - - def addComment(self, task_id, comment): - return self.client.stories.create_on_task(task_id, {"text": comment}) - - def _typeahead(self, workspace, asana_type, query, count=5): - return self.client.workspaces.typeahead(workspace, { - 'type': asana_type, - 'query': query, - 'count': count - }) - - def userTypeahead(self, workspace, query, count): - return self._typeahead(workspace, 'user', query, count) - - def projectTypeahead(self, workspace, query, opt_count): - return self._typeahead(workspace, 'project', query, count=opt_count) diff --git a/ui.py b/ui.py deleted file mode 100644 index 221f915..0000000 --- a/ui.py +++ /dev/null @@ -1,365 +0,0 @@ -# -*- coding: latin-1 -*- -import urwid -import sys - -# TaskEdit modes -EDIT = 'edit' -LIST = 'list' - -palette = [ - ('selected', 'standout', ''), - ('selected workspace', 'standout,bold', ''), - ('header', 'bold,light green', ''), - ('secondary', 'light gray', ''), - ('task', 'light green', ''), - ('project', 'yellow', ''), - ('section', 'white', 'dark green'), - ('workspace', 'white', 'dark blue'), - ('pager', 'standout', ''), -] - -class WorkspaceMenu(urwid.Columns): - def __init__(self, workspaces): - super(WorkspaceMenu, self).__init__([], dividechars=1) - - for workspace in workspaces: - button = WorkspaceButton(workspace, self.loadWorkspace) - self.contents.append((urwid.AttrMap(button, - None, - focus_map='selected workspace'), - self.options('given', 24))) - def keypress(self, size, key): - if key == 'j': - key = 'down' - elif key == 'h': - key = 'left' - elif key == 'l': - key = 'right' - return super(WorkspaceMenu, self).keypress(size, key) - - def loadWorkspace(self, widget, workspace_id): - urwid.emit_signal(self, 'click', workspace_id) - -class WorkspaceButton(urwid.Button): - def __init__(self, workspace, onClick): - super(WorkspaceButton, self).__init__(workspace['name']) - urwid.connect_signal(self, 'click', onClick, workspace['id']) - -class PagerButton(urwid.Button): - def __init__(self, loadPage): - super(PagerButton, self).__init__(('pager', 'load more')) - urwid.connect_signal(self, 'click', loadPage) - -class TypeAheadButton(urwid.Button): - def __init__(self, item, onClick): - super(TypeAheadButton, self).__init__(item['name']) - urwid.connect_signal(self, 'click', onClick, item) - -class ProjectIcon(urwid.SelectableIcon): - def __init__(self, project, onClick): - self.project = project - self.onClick = onClick - super(ProjectIcon, self).__init__(project['name']) - - def keypress(self, size, key): - if key in ('enter', 'right', 'l'): - self.onClick(self.project['id']) - else: - return super(ProjectIcon, self).keypress(size, key) - -class ProjectList(urwid.ListBox): - def __init__(self, projects): - self.projects = projects - - body = urwid.SimpleFocusListWalker( - [urwid.AttrMap(ProjectIcon({'name': 'My Tasks', 'id': None}, - self.loadProject), 'project'), - None] - ) - super(ProjectList, self).__init__(body) - self.loadPage() - - def loadPage(self, opt=None): - self.body.pop() - for i in range(50): - try: - self.body.append(urwid.AttrMap(ProjectIcon(self.projects.next(), - self.loadProject), 'project')) - except StopIteration: - return - - self.body.append(PagerButton(self.loadPage)) - - def keypress(self, size, key): - if key == 'j': - key = 'down' - elif key == 'k': - key = 'up' - - return super(ProjectList, self).keypress(size, key) - - def loadProject(self, project_id): - urwid.emit_signal(self, 'loadproject', project_id) - - -class TaskList(urwid.ListBox): - def __init__(self, tasks): - self.tasks = tasks - - task_widgets = urwid.Pile( - [TaskEdit(task) for task in tasks] - ) - - body = urwid.SimpleFocusListWalker([]) - for task_widget,_ in task_widgets.contents: - self.connectSignals(task_widget) - style = 'section' if len(task_widget.task['name']) and \ - task_widget.task['name'][-1] == ':' else 'task' - body.append(urwid.AttrMap(task_widget, style, focus_map='selected')) - - super(TaskList, self).__init__(body) - - def insertNewTask(self, task): - task_widget = TaskEdit(task, mode=EDIT) - self.connectSignals(task_widget) - index = self.focus_position + 1 - self.body.insert(index, - urwid.AttrMap(task_widget, 'task', - focus_map='selected')) - self.focus_position += 1 - - def connectSignals(self, task_widget): - urwid.connect_signal(task_widget, 'complete', self.completeTask) - urwid.connect_signal(task_widget, 'newtask', self.newTask) - urwid.connect_signal(task_widget, 'updatetask', self.updateTask) - urwid.connect_signal(task_widget, 'details', self.details) - - def completeTask(self, task_id): - urwid.emit_signal(self, 'complete', task_id) - del self.body[self.focus_position] - - def newTask(self, task_after_id=None): - urwid.emit_signal(self, 'newtask', task_after_id) - - def updateTask(self, task_id, name): - urwid.emit_signal(self, 'updatetask', task_id, name) - - def details(self, task_id): - urwid.emit_signal(self, 'details', task_id) - - def keypress(self, size, key): - # The ListBox will handle scrolling for us, so we trick it into thinking - # it's being passed arrow keys - if self.focus.original_widget.mode == LIST: - if key == 'j': - key = 'down' - elif key == 'k': - key = 'up' - - key = super(TaskList, self).keypress(size, key) - - if key in ('q', 'Q'): - raise urwid.ExitMainLoop() - else: - return key - -class TaskEdit(urwid.Edit): - completed = False - def __init__(self, task, mode=LIST): - self.task = task - self.mode = mode - super(TaskEdit, self).__init__(task["name"]) - - def keypress(self, size, key): - if self.mode == EDIT: - key = super(TaskEdit, self).keypress(size, key) - - if key in ('esc', 'up', 'down'): - self.mode = LIST - self.set_caption(self.edit_text) - self.set_edit_text('') - urwid.emit_signal(self, 'updatetask', self.task['id'], - self.caption) - if key != 'esc': - return key - else: - if key == 'i': - if self.completed: - return - self.mode = EDIT - self.set_edit_text(self.caption) - self.set_caption('') - elif key == 'tab': - urwid.emit_signal(self, 'complete', self.task['id']) - elif key == 'enter': - urwid.emit_signal(self, 'newtask', self.task['id']) - elif key in ('l', 'right'): - urwid.emit_signal(self, 'details', self.task['id']) - else: - return key - -class CommentEdit(urwid.Edit): - def __init__(self, task): - self.task = task - super(CommentEdit, self).__init__(('secondary', u'Add a comment:\n')) - - def keypress(self, size, key): - if key != 'enter': - return super(CommentEdit, self).keypress(size, key) - urwid.emit_signal(self, 'comment', self.task['id'], self.edit_text) - -class TaskNameEdit(urwid.Edit): - def __init__(self, task): - self.task = task - super(TaskNameEdit, self).__init__(('secondary', - u'#' + str(task['id']) + ' '), - task['name']) - - def keypress(self, size, key): - if key in ('enter', 'esc', 'up', 'down'): - if (self.edit_text != self.task['name']): - urwid.emit_signal(self, 'updatetask', self.task['id'], - self.edit_text) - return super(TaskNameEdit, self).keypress(size, key) - -class DescriptionEdit(urwid.Edit): - def __init__(self, task): - self.task = task - super(DescriptionEdit, self).__init__(('secondary', u'Description:\n'), - task['notes'], - multiline=True) - - def keypress(self, size, key): - if key != 'esc': - return super(DescriptionEdit, self).keypress(size, key) - urwid.emit_signal(self, 'updatedescription', self.task['id'], - self.edit_text) - -class AssigneeTypeAhead(urwid.Pile): - def __init__(self, task): - self.task = task - - if task['assignee'] != None: - assignee = task['assignee']['name'] - else: - assignee = "" - - self.edit = urwid.Edit('Assignee: ', assignee) - urwid.connect_signal(self.edit, 'change', self.typeAhead) - - body = [('pack', self.edit)] - - super(AssigneeTypeAhead, self).__init__(body) - - def typeAhead(self, widget, text): - urwid.emit_signal(self, 'usertypeahead', text, self.updateTypeAhead) - - def updateTypeAhead(self, users): - users = [(TypeAheadButton(u, self.assign), ('pack', None)) for u in users] - - users.insert(0, self.contents[0]) - - self.contents = users - - def assign(self, widget, user): - urwid.emit_signal(self, 'assigntask', self.task['id'], user['id']) - self.contents = [self.contents[0]] - self.edit.set_edit_text(user['name']) - -class TaskDetails(urwid.ListBox): - def __init__(self, task, stories, subtasks): - self.task = task - self.stories = stories - - comment_edit = CommentEdit(task) - urwid.connect_signal(comment_edit, 'comment', self.comment) - - self.description_edit = DescriptionEdit(task) - urwid.connect_signal(self.description_edit, 'updatedescription', - self.updateDescription) - - task_name_edit = TaskNameEdit(task) - urwid.connect_signal(task_name_edit, 'updatetask', self.updateTask) - - assignee_type_ahead = AssigneeTypeAhead(task) - urwid.connect_signal(assignee_type_ahead, 'usertypeahead', - self.userTypeAhead) - urwid.connect_signal(assignee_type_ahead, 'assigntask', self.assignTask) - - projects = [urwid.AttrMap(ProjectIcon(project, self.loadProject), - 'project') - for project in task['projects']] - - if task['parent'] != None: - parent = TaskEdit(task['parent']) - urwid.connect_signal(parent, 'updatetask', self.updateSubtask) - urwid.connect_signal(parent, 'details', self.showDetails) - - #Remap enter to load details of parent - urwid.connect_signal(parent, 'newtask', self.showDetails) - projects.append(parent) - - all_subtasks = [t for t in subtasks] - subtask_list = TaskList(all_subtasks) - urwid.connect_signal(subtask_list, 'complete', self.completeTask) - urwid.connect_signal(subtask_list, 'newtask', self.newTask) - urwid.connect_signal(subtask_list, 'updatetask', self.updateSubtask) - urwid.connect_signal(subtask_list, 'details', self.showDetails) - - body = projects + \ - [ - urwid.Divider('='), - task_name_edit, - assignee_type_ahead, - urwid.Divider('-'), - self.description_edit, - urwid.Divider('-'), - urwid.BoxAdapter(subtask_list, len(all_subtasks)), - urwid.Divider('-'), - ] + \ - [urwid.Text('[' + story['created_by']['name'] + '] ' + \ - story['text']) for story in stories] + \ - [comment_edit] - - super(TaskDetails, self).__init__(body) - - def completeTask(self, task_id): - urwid.emit_signal(self, 'complete', task_id) - del self.body[self.focus_position] - - def newTask(self, task_after_id=None): - urwid.emit_signal(self, 'newtask', task_after_id) - - def updateSubtask(self, task_id, name): - urwid.emit_signal(self, 'updatetask', task_id, name) - - def showDetails(self, task_id): - urwid.emit_signal(self, 'details', task_id) - - def keypress(self, size, key): - key = super(TaskDetails, self).keypress(size, key) - - if self.focus != self.description_edit and \ - self.description_edit.edit_text != self.task['notes']: - self.updateDescription(self.task['id'], - self.description_edit.edit_text) - - return key - - def comment(self, task_id, comment): - urwid.emit_signal(self, 'comment', task_id, comment) - - def updateDescription(self, task_id, description): - urwid.emit_signal(self, 'updatedescription', task_id, description) - - def updateTask(self, task_id, name): - urwid.emit_signal(self, 'updatetask', task_id, name) - - def loadProject(self, project_id): - urwid.emit_signal(self, 'loadproject', project_id) - - def userTypeAhead(self, text, callback): - urwid.emit_signal(self, 'usertypeahead', text, callback) - - def assignTask(self, task_id, user_id): - urwid.emit_signal(self, 'assigntask', task_id, user_id)