diff --git a/asana_service.py b/asana_service.py index b6446cd..0911f67 100644 --- a/asana_service.py +++ b/asana_service.py @@ -3,6 +3,7 @@ from models.models import * class AsanaService(object): TASK_FIELDS = [ 'name', + 'html_notes', 'notes', 'assignee.name', 'assignee_status', @@ -23,6 +24,13 @@ class AsanaService(object): 'subtasks.name', ] + STORY_FIELDS = [ + 'created_by.name', + 'html_text', + 'text', + 'type' + ] + def __init__(self, client): self.client = client self.completed_tasks = False @@ -32,17 +40,6 @@ class AsanaService(object): def __wrap__(self, Klass, values): return map(Klass, values) - def get_tasks(self, project_id): - params = { - 'completed_since': '' if self.completed_tasks else 'now', - 'opt_fields': self.TASK_FIELDS, - } - - return self.__wrap__( - Task, - self.client.tasks.find_by_project(project_id, params=params) - ) - def get_my_tasks(self): return self.__wrap__( Task, @@ -54,6 +51,11 @@ class AsanaService(object): }) ) + def get_project(self, project_id): + return Project( + self.client.projects.find_by_id(project_id) + ) + def get_task(self, task_id): return Task(self.client.tasks.find_by_id( task_id, @@ -61,8 +63,21 @@ class AsanaService(object): 'opt_fields': self.TASK_FIELDS } )) + + def get_tasks(self, project_id): + params = { + 'completed_since': '' if self.completed_tasks else 'now', + 'opt_fields': self.TASK_FIELDS, + } + + return self.__wrap__( + Task, + self.client.tasks.find_by_project(project_id, params=params) + ) def get_stories(self, task_id): - stories = self.client.stories.find_by_task(task_id) + stories = self.client.stories.find_by_task(task_id, params = { + 'opt_fields': self.STORY_FIELDS + }) filtered_stories = filter(lambda s: s['type'] == 'comment', stories) return self.__wrap__(Story, filtered_stories) diff --git a/models/models.py b/models/models.py index e039178..1036dda 100644 --- a/models/models.py +++ b/models/models.py @@ -1,4 +1,5 @@ import dateutil +from html.parser import HTMLParser import sys class AsanaObject(object): @@ -34,10 +35,15 @@ class Task(AsanaObject): return self.object_dict['assignee_status'] def description(self): - if 'notes' in self.object_dict: + if 'html_notes' in self.object_dict: + parser = HTMLTextParser() + parser.feed(self.object_dict['html_notes']) + parser.close() + return parser.get_formatted_text() + elif 'notes' in self.object_dict: return self.object_dict['notes'] else: - return None + return "" def due_date(self): if 'due_at' in self.object_dict: @@ -89,6 +95,81 @@ class CustomField(AsanaObject): return '' +class Strong(object): + def __init__(self, body): + self.body = body + + def text_format(self): + return ('strong', self.body.text_format()) + +class Italic(object): + def __init__(self, body): + self.body = body + + def text_format(self): + return ('italic', self.body.text_format()) + +class Underline(object): + def __init__(self, body): + self.body = body + + def text_format(self): + return ('underline', self.body.text_format()) + +class Link(object): + def __init__(self, body): + self.body = body + + def text_format(self): + return ('link', self.body.text_format()) + +class Tag(object): + def __init__(self, body): + self.body = body + + def text_format(self): + return self.body.text_format() + +class Text(object): + def __init__(self, body): + self.body = body + + def text_format(self): + return self.body + +class HTMLTextParser(HTMLParser): + def __init__(self): + self.text = [] + self.tag_stack = [] + super().__init__() + + def handle_starttag(self, tag, attrs): + if tag == 'strong': + self.tag_stack.append(Strong) + elif tag == 'em': + self.tag_stack.append(Italic) + elif tag == 'u': + self.tag_stack.append(Underline) + elif tag == 'a': + self.tag_stack.append(Link) + else: + self.tag_stack.append(Tag) + + def handle_data(self, data): + self.text.append(Text(data)) + + def handle_endtag(self, tag): + data = self.text.pop() + tag = self.tag_stack.pop() + + self.text.append(tag(data)) + + def get_formatted_text(self): + formatted = [t.text_format() for t in self.text] + print(formatted, file=sys.stderr) + return formatted + + class Story(AsanaObject): def creator(self): if 'created_by' in self.object_dict: @@ -97,4 +178,10 @@ class Story(AsanaObject): return '' def text(self): - return self.object_dict['text'] + if 'html_text' in self.object_dict: + parser = HTMLTextParser() + parser.feed(self.object_dict['html_text']) + parser.close() + return parser.get_formatted_text() + else: + return [self.object_dict['text']] diff --git a/ui/constants.py b/ui/constants.py index 38fd8ad..8d793b5 100644 --- a/ui/constants.py +++ b/ui/constants.py @@ -8,6 +8,10 @@ palette = [ ('selected', 'standout', ''), ('task', 'light green', ''), ('text', '', ''), + ('strong', 'bold', ''), + ('underline', 'underline', ''), + ('link', 'underline,light blue', ''), + ('italic', 'italics', ''), ('workspace', 'white', 'dark blue'), ] diff --git a/ui/task_details.py b/ui/task_details.py index a534399..a7ad9e7 100644 --- a/ui/task_details.py +++ b/ui/task_details.py @@ -74,11 +74,9 @@ class CustomFields(object): class Stories(object): def __init__(self, stories): - components = [urwid.Text([ - ('author', s.creator()), - s.text(), - '\n' - ]) for s in stories] + components = [ + urwid.Text([('author', s.creator())] + s.text()) + for s in stories] self.stories = urwid.Pile(components) diff --git a/ui/ui.py b/ui/ui.py index 7f96971..062689d 100644 --- a/ui/ui.py +++ b/ui/ui.py @@ -39,9 +39,10 @@ class Ui(object): def task_list(self, id): self.nav_stack.append(('project', id)) def runInThread(): + project = self.asana_service.get_project(id) tasks = self.asana_service.get_tasks(id) self.update(TaskList(tasks, - 'TODO: get project name', + project.name(), self.task_details ).component()) thread = Thread(target=runInThread())