Compare commits
7 Commits
assignee-d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 04a6361d6b | |||
|
1ed1ca1065
|
|||
|
2a9b674cc5
|
|||
|
8a86d4b559
|
|||
|
da9dd3578f
|
|||
| 364a69fb3b | |||
| 47eff82d3f |
@@ -5,7 +5,7 @@ A curses CLI for Asana, using the Asana API.
|
||||
* python 3
|
||||
* [python-asana](https://github.com/Asana/python-asana)
|
||||
* [urwid](http://urwid.org)
|
||||
* [pyhon-dateutil](https://github.com/dateutil/dateutil/)
|
||||
* [python-dateutil](https://github.com/dateutil/dateutil/)
|
||||
|
||||
## Setup
|
||||
### Create an Asana OAuth app
|
||||
|
||||
@@ -25,6 +25,7 @@ class AsanaService(object):
|
||||
]
|
||||
|
||||
STORY_FIELDS = [
|
||||
'created_at',
|
||||
'created_by.name',
|
||||
'html_text',
|
||||
'text',
|
||||
@@ -74,7 +75,7 @@ class AsanaService(object):
|
||||
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, params = {
|
||||
'opt_fields': self.STORY_FIELDS
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import dateutil
|
||||
from datetime import timezone
|
||||
import dateutil.parser
|
||||
from html.parser import HTMLParser
|
||||
import sys
|
||||
|
||||
@@ -26,7 +27,7 @@ class Task(AsanaObject):
|
||||
return super(Task, self).name()
|
||||
|
||||
def assignee(self):
|
||||
if 'assignee' in self.object_dict:
|
||||
if 'assignee' in self.object_dict and self.object_dict['assignee']:
|
||||
return User(self.object_dict['assignee'])
|
||||
else:
|
||||
return None
|
||||
@@ -50,12 +51,15 @@ class Task(AsanaObject):
|
||||
return ""
|
||||
|
||||
def due_date(self):
|
||||
if 'due_at' in self.object_dict:
|
||||
return dateutil.parser.parse(self.object_dict['due_at'])
|
||||
elif 'due_one' in self.object_dict:
|
||||
return dateutil.parser.parse(self.object_dict['due_on'])
|
||||
if 'due_at' in self.object_dict and self.object_dict['due_at']:
|
||||
datetime = dateutil.parser.parse(self.object_dict['due_at'])
|
||||
datetime = datetime.replace(tzinfo=timezone.utc).astimezone(tz=None)
|
||||
return datetime.strftime('%b %d, %Y %H:%M')
|
||||
elif 'due_on' in self.object_dict and self.object_dict['due_on']:
|
||||
date = dateutil.parser.parse(self.object_dict['due_on'])
|
||||
return date.strftime('%b %d, %Y')
|
||||
else:
|
||||
return None
|
||||
return 'no due date'
|
||||
|
||||
def parent(self):
|
||||
if 'parent' in self.object_dict and self.object_dict['parent']:
|
||||
@@ -184,7 +188,7 @@ class HTMLTextParser(HTMLParser):
|
||||
self.text.append(Text(data))
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
data = self.text.pop()
|
||||
data = self.text.pop() if len(self.text) > 0 else Text("")
|
||||
Class = self.tag_stack.pop()
|
||||
|
||||
if tag == 'ul' or tag =='ol':
|
||||
@@ -207,6 +211,13 @@ class Story(AsanaObject):
|
||||
else:
|
||||
return ''
|
||||
|
||||
def created_at(self):
|
||||
if 'created_at' in self.object_dict:
|
||||
return dateutil.parser.parse(self.object_dict['created_at']) \
|
||||
.replace(tzinfo=timezone.utc).astimezone(tz=None)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def text(self):
|
||||
if 'html_text' in self.object_dict:
|
||||
parser = HTMLTextParser()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
asana==0.7.0
|
||||
python-dateutil==2.6.1
|
||||
asana==0.8.2
|
||||
python-dateutil==2.8.0
|
||||
urwid==2.0.1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
palette = [
|
||||
('atm_section', 'white,bold', 'dark blue'),
|
||||
('author', 'bold,dark blue', ''),
|
||||
('timestamp', 'underline', ''),
|
||||
('custom_fields', 'dark red', ''),
|
||||
('header', 'bold,light green', ''),
|
||||
('project', 'yellow', ''),
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import urwid
|
||||
from datetime import date, datetime
|
||||
|
||||
from ui.task_list import TaskRow
|
||||
|
||||
class TaskDetails(object):
|
||||
def __init__(self, task, stories, on_subtask_click, on_project_click,
|
||||
on_comment):
|
||||
on_comment, on_assignee_click, on_due_date_click):
|
||||
self.task = task
|
||||
self.on_subtask_click = on_subtask_click,
|
||||
self.on_project_click = on_project_click,
|
||||
@@ -12,13 +13,15 @@ class TaskDetails(object):
|
||||
|
||||
body = [
|
||||
urwid.Text(('task', task.name())),
|
||||
urwid.Divider('-'),
|
||||
urwid.Divider('⎼'),
|
||||
Memberships(task, on_subtask_click, on_project_click).component(),
|
||||
urwid.Divider('-'),
|
||||
urwid.Divider('⎼'),
|
||||
Assignee(task, on_assignee_click).component(),
|
||||
DueDate(task, on_due_date_click).component(),
|
||||
CustomFields(task).component(),
|
||||
urwid.Divider('='),
|
||||
urwid.Divider('⎼'),
|
||||
urwid.Text(task.description()),
|
||||
urwid.Divider('-'),
|
||||
urwid.Divider('⎼'),
|
||||
]
|
||||
|
||||
if task.subtasks():
|
||||
@@ -38,6 +41,45 @@ class TaskDetails(object):
|
||||
def component(self):
|
||||
return self.details
|
||||
|
||||
class Assignee(object):
|
||||
def __init__(self, task, on_click):
|
||||
if task.assignee():
|
||||
assignee = task.assignee().name()
|
||||
else:
|
||||
assignee = "unassigned"
|
||||
|
||||
|
||||
self.assignee = urwid.SelectableIcon([('strong', 'Assignee: '), ('', assignee)])
|
||||
|
||||
self.on_click = on_click
|
||||
#urwid.connect_signal(self.assignee, 'keypress', self.on_keypress)
|
||||
|
||||
def component(self):
|
||||
return self.assignee
|
||||
|
||||
def on_keypress(self, size, key):
|
||||
if key == "enter":
|
||||
self.on_click()
|
||||
else:
|
||||
return key
|
||||
|
||||
class DueDate(object):
|
||||
def __init__(self, task, on_click):
|
||||
due_date = task.due_date()
|
||||
self.due_date = urwid.SelectableIcon([('strong', 'Due: '), ('', str(task.due_date()))])
|
||||
|
||||
self.on_click = on_click
|
||||
#urwid.connect_signal(self.due_date, 'keypress', self.on_keypress)
|
||||
|
||||
def component(self):
|
||||
return self.due_date
|
||||
|
||||
def on_keypress(self, size, key):
|
||||
if key == "enter":
|
||||
self.on_click()
|
||||
else:
|
||||
return key
|
||||
|
||||
class Memberships(object):
|
||||
def __init__(self, task, on_subtask_click, on_project_click):
|
||||
self.on_project_click = on_project_click
|
||||
@@ -75,7 +117,11 @@ class CustomFields(object):
|
||||
class Stories(object):
|
||||
def __init__(self, stories):
|
||||
components = [
|
||||
urwid.Text([('author', s.creator())] + s.text())
|
||||
urwid.Text([
|
||||
('timestamp', s.created_at().strftime('%Y-%m-%d %H:%M')),
|
||||
' ',
|
||||
('author', s.creator()),
|
||||
] + s.text())
|
||||
for s in stories]
|
||||
|
||||
self.stories = urwid.Pile(components)
|
||||
|
||||
Reference in New Issue
Block a user