[WIP] Refactor code to be more modular
This commit is contained in:
61
auth.py
Normal file
61
auth.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import asana
|
||||||
|
from asana.session import AsanaOAuth2Session
|
||||||
|
|
||||||
|
from secrets import CLIENT_ID, CLIENT_SECRET
|
||||||
|
|
||||||
|
class Auth:
|
||||||
|
def __init__(self):
|
||||||
|
try:
|
||||||
|
f = open(".oauth", "r")
|
||||||
|
token = json.loads(f.readline())
|
||||||
|
f.close()
|
||||||
|
self.client = asana.Client.oauth(
|
||||||
|
client_id=CLIENT_ID,
|
||||||
|
client_secret=CLIENT_SECRET,
|
||||||
|
token=token,
|
||||||
|
token_updater=self.saveToken,
|
||||||
|
auto_refresh_url=AsanaOAuth2Session.token_url,
|
||||||
|
auto_refresh_kwargs={
|
||||||
|
'client_id': CLIENT_ID,
|
||||||
|
'client_secret': CLIENT_SECRET
|
||||||
|
},
|
||||||
|
)
|
||||||
|
except IOError:
|
||||||
|
self.getToken()
|
||||||
|
|
||||||
|
def saveToken(self, token):
|
||||||
|
f = open('.oauth', 'w')
|
||||||
|
f.write(json.dumps(token))
|
||||||
|
f.close()
|
||||||
|
os.chmod('.oauth', 0o600)
|
||||||
|
|
||||||
|
def getToken(self):
|
||||||
|
self.client = asana.Client.oauth(
|
||||||
|
client_id=CLIENT_ID,
|
||||||
|
client_secret=CLIENT_SECRET,
|
||||||
|
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
|
||||||
|
token_updater=self.saveToken,
|
||||||
|
auto_refresh_url=AsanaOAuth2Session.token_url,
|
||||||
|
auto_refresh_kwargs={
|
||||||
|
'client_id': CLIENT_ID,
|
||||||
|
'client_secret': CLIENT_SECRET
|
||||||
|
},
|
||||||
|
)
|
||||||
|
(url, state) = self.client.session.authorization_url()
|
||||||
|
print("Go to the following link and enter the code:")
|
||||||
|
print(url)
|
||||||
|
try:
|
||||||
|
import webbrowser
|
||||||
|
webbrowser.open(url)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
code = sys.stdin.readline().strip()
|
||||||
|
token = self.client.session.fetch_token(code=code)
|
||||||
|
self.saveToken(token)
|
||||||
|
|
||||||
|
def getClient(self):
|
||||||
|
return self.client
|
||||||
146
cmdasana.py
146
cmdasana.py
@@ -1,74 +1,23 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: latin-1 -*-
|
# -*- coding: latin-1 -*-
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
import urwid
|
import urwid
|
||||||
import asana
|
import asana
|
||||||
from asana.session import AsanaOAuth2Session
|
|
||||||
from oauthlib.oauth2.rfc6749.errors import TokenExpiredError
|
|
||||||
|
|
||||||
import ui
|
import ui
|
||||||
from secrets import CLIENT_ID, CLIENT_SECRET
|
from auth import Auth
|
||||||
|
from data import Data
|
||||||
# id of the personal projects domain
|
|
||||||
PERSONAL = 498346170860
|
|
||||||
|
|
||||||
class CmdAsana:
|
class CmdAsana:
|
||||||
loop = None
|
loop = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
try:
|
auth = Auth()
|
||||||
f = open(".oauth", "r")
|
self.data = Data(auth.getClient())
|
||||||
token = json.loads(f.readline())
|
|
||||||
f.close()
|
|
||||||
self.client = asana.Client.oauth(
|
|
||||||
client_id=CLIENT_ID,
|
|
||||||
client_secret=CLIENT_SECRET,
|
|
||||||
token=token,
|
|
||||||
token_updater=self.saveToken,
|
|
||||||
auto_refresh_url=AsanaOAuth2Session.token_url,
|
|
||||||
auto_refresh_kwargs={
|
|
||||||
'client_id': CLIENT_ID,
|
|
||||||
'client_secret': CLIENT_SECRET
|
|
||||||
},
|
|
||||||
)
|
|
||||||
except IOError:
|
|
||||||
self.getToken()
|
|
||||||
|
|
||||||
self.me = self.client.users.me()
|
|
||||||
|
|
||||||
def saveToken(self, token):
|
|
||||||
f = open('.oauth', 'w')
|
|
||||||
f.write(json.dumps(token))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def getToken(self):
|
|
||||||
self.client = asana.Client.oauth(
|
|
||||||
client_id=CLIENT_ID,
|
|
||||||
client_secret=CLIENT_SECRET,
|
|
||||||
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
|
|
||||||
token_updater=self.saveToken,
|
|
||||||
auto_refresh_url=AsanaOAuth2Session.token_url,
|
|
||||||
auto_refresh_kwargs={
|
|
||||||
'client_id': CLIENT_ID,
|
|
||||||
'client_secret': CLIENT_SECRET
|
|
||||||
},
|
|
||||||
)
|
|
||||||
(url, state) = self.client.session.authorization_url()
|
|
||||||
print("Go to the following link and enter the code:")
|
|
||||||
print(url)
|
|
||||||
try:
|
|
||||||
import webbrowser
|
|
||||||
webbrowser.open(url)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
code = sys.stdin.readline().strip()
|
|
||||||
token = self.client.session.fetch_token(code=code)
|
|
||||||
self.saveToken(token)
|
|
||||||
|
|
||||||
def saveState(self):
|
def saveState(self):
|
||||||
f = open('.state', 'w')
|
f = open('.state', 'w')
|
||||||
@@ -88,44 +37,17 @@ class CmdAsana:
|
|||||||
'workspace_id': workspace_id
|
'workspace_id': workspace_id
|
||||||
}
|
}
|
||||||
|
|
||||||
def myWorkspaces(self):
|
|
||||||
return self.me['workspaces']
|
|
||||||
|
|
||||||
def allMyTasks(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 projectTasks(self, project_id):
|
|
||||||
return self.client.tasks.find_by_project(project_id, params={
|
|
||||||
'completed_since': 'now'
|
|
||||||
})
|
|
||||||
|
|
||||||
def completeTask(self, task_id):
|
|
||||||
self.client.tasks.update(task_id, completed=True)
|
|
||||||
|
|
||||||
def newTask(self, task_after_id):
|
def newTask(self, task_after_id):
|
||||||
def runInThread():
|
def runInThread():
|
||||||
if self.state['view'] == 'project':
|
if self.state['view'] == 'project':
|
||||||
task = self.client.tasks.create_in_workspace(
|
task = self.data.createTask(
|
||||||
self.state['workspace_id'],
|
self.state['workspace_id'],
|
||||||
projects=[self.state['id']]
|
opt_projects=[self.state['id']],
|
||||||
|
opt_after=task_after_id
|
||||||
)
|
)
|
||||||
if task_after_id != None:
|
|
||||||
self.client.tasks.add_project(task['id'],
|
|
||||||
project=self.state['id'],
|
|
||||||
insert_after=task_after_id)
|
|
||||||
else:
|
else:
|
||||||
task = self.client.tasks.create_in_workspace(
|
task = self.data.createTask(
|
||||||
self.state['workspace_id'],
|
self.state['workspace_id'],
|
||||||
assignee=self.me['id']
|
assignee=self.me['id']
|
||||||
)
|
)
|
||||||
@@ -140,30 +62,21 @@ class CmdAsana:
|
|||||||
thread = Thread(target=runInThread)
|
thread = Thread(target=runInThread)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def updateTask(self, task_id, name):
|
def updateTask(self, task_id, opt_name=None, opt_projects=None,
|
||||||
|
opt_assignee=None, opt_notes=None):
|
||||||
def runInThread():
|
def runInThread():
|
||||||
self.client.tasks.update(task_id, name=name)
|
self.data.updateTask(task_id,
|
||||||
|
opt_name=opt_name,
|
||||||
thread = Thread(target=runInThread)
|
opt_projects=opt_projects,
|
||||||
thread.start()
|
opt_assignee=opt_assignee,
|
||||||
|
opt_notes=opt_notes)
|
||||||
def updateDescription(self, task_id, description):
|
|
||||||
def runInThread():
|
|
||||||
self.client.tasks.update(task_id, notes=description)
|
|
||||||
|
|
||||||
thread = Thread(target=runInThread)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
def assignTask(self, task_id, user_id):
|
|
||||||
def runInThread():
|
|
||||||
self.client.tasks.update(task_id, assignee=user_id)
|
|
||||||
|
|
||||||
thread = Thread(target=runInThread)
|
thread = Thread(target=runInThread)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def addComment(self, task_id, comment):
|
def addComment(self, task_id, comment):
|
||||||
def runInThread():
|
def runInThread():
|
||||||
self.client.stories.create_on_task(task_id, {"text": comment})
|
self.data.addComment(task_id, comment)
|
||||||
self.showDetails(task_id, show_loading=False)
|
self.showDetails(task_id, show_loading=False)
|
||||||
|
|
||||||
thread = Thread(target=runInThread)
|
thread = Thread(target=runInThread)
|
||||||
@@ -171,18 +84,7 @@ class CmdAsana:
|
|||||||
|
|
||||||
def userTypeAhead(self, text, callback):
|
def userTypeAhead(self, text, callback):
|
||||||
def runInThread():
|
def runInThread():
|
||||||
if self.state['workspace_id'] != PERSONAL:
|
callback(self.data.userTypeahead(self.state['workspace_id'], text))
|
||||||
users = self.client.workspaces \
|
|
||||||
.typeahead(self.state['workspace_id'],
|
|
||||||
{
|
|
||||||
'type': 'user',
|
|
||||||
'query': text,
|
|
||||||
'count': 5
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
users = [self.me]
|
|
||||||
|
|
||||||
callback(users)
|
|
||||||
self.loop.draw_screen()
|
self.loop.draw_screen()
|
||||||
|
|
||||||
thread = Thread(target=runInThread)
|
thread = Thread(target=runInThread)
|
||||||
@@ -209,7 +111,7 @@ class CmdAsana:
|
|||||||
self.showMainLoading()
|
self.showMainLoading()
|
||||||
|
|
||||||
def runInThread():
|
def runInThread():
|
||||||
tasks = self.allMyTasks(workspace_id)
|
tasks = self.data.myTasks(workspace_id)
|
||||||
update(tasks)
|
update(tasks)
|
||||||
|
|
||||||
def update(tasks):
|
def update(tasks):
|
||||||
@@ -229,7 +131,7 @@ class CmdAsana:
|
|||||||
self.showMainLoading()
|
self.showMainLoading()
|
||||||
|
|
||||||
def runInThread():
|
def runInThread():
|
||||||
tasks = self.projectTasks(project_id)
|
tasks = self.data.tasksInProject(project_id)
|
||||||
update(tasks)
|
update(tasks)
|
||||||
|
|
||||||
def update(tasks):
|
def update(tasks):
|
||||||
@@ -249,7 +151,7 @@ class CmdAsana:
|
|||||||
self.showMainLoading()
|
self.showMainLoading()
|
||||||
|
|
||||||
def runInThread():
|
def runInThread():
|
||||||
projects = self.allMyProjects()
|
projects = self.data.allMyProjects()
|
||||||
update(projects)
|
update(projects)
|
||||||
|
|
||||||
def update(projects):
|
def update(projects):
|
||||||
@@ -268,9 +170,7 @@ class CmdAsana:
|
|||||||
self.showMainLoading()
|
self.showMainLoading()
|
||||||
|
|
||||||
def runInThread():
|
def runInThread():
|
||||||
task = self.client.tasks.find_by_id(task_id)
|
task, stories, subtasks = self.data.getTask(task_id)
|
||||||
stories = self.client.stories.find_by_task(task_id)
|
|
||||||
subtasks = self.client.tasks.subtasks(task_id)
|
|
||||||
update(task, stories, subtasks)
|
update(task, stories, subtasks)
|
||||||
|
|
||||||
def update(task, stories, subtasks):
|
def update(task, stories, subtasks):
|
||||||
@@ -355,7 +255,7 @@ class CmdAsana:
|
|||||||
urwid.set_encoding("UTF-8")
|
urwid.set_encoding("UTF-8")
|
||||||
self.registerSignals()
|
self.registerSignals()
|
||||||
|
|
||||||
workspace_menu = ui.WorkspaceMenu(self.myWorkspaces())
|
workspace_menu = ui.WorkspaceMenu(self.data.myWorkspaces())
|
||||||
urwid.connect_signal(workspace_menu, 'click', self.showProjectList)
|
urwid.connect_signal(workspace_menu, 'click', self.showProjectList)
|
||||||
|
|
||||||
self.frame = urwid.Pile([
|
self.frame = urwid.Pile([
|
||||||
|
|||||||
89
data.py
Normal file
89
data.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
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)
|
||||||
Reference in New Issue
Block a user