2 Wrapper for Remember The Milk API
10 return "/".join(rturl.split(r"\/"))
13 class RtMilkManager(object):
15 Interface with rememberthemilk.com
17 @todo Decide upon an interface that will end up a bit less bloated
18 @todo Add interface for task tags
19 @todo Add interface for postponing tasks (have way for UI to specify how many days to postpone?)
20 @todo Add interface for task recurrence
21 @todo Add interface for task estimate
22 @todo Add interface for task location
23 @todo Add interface for task url
24 @todo Add interface for task notes
25 @todo Add undo support
27 API_KEY = '71f471f7c6ecdda6def341967686fe05'
28 SECRET = '7d3248b085f7efbe'
30 def __init__(self, username, password, token):
31 self._username = username
32 self._password = password
35 self._rtm = rtmapi.RTMapi(self._username, self.API_KEY, self.SECRET, token)
37 resp = self._rtm.timelines.create()
38 self._timeline = resp.timeline
41 def get_projects(self):
42 if len(self._lists) == 0:
43 self._populate_projects()
45 for list in self._lists:
48 def get_project(self, projId):
49 proj = [proj for proj in self.get_projects() if projId == proj["id"]]
50 assert len(proj) == 1, "%r / %r" % (proj, self._lists)
53 def get_project_names(self):
54 return (list["name"] for list in self.get_projects)
56 def lookup_project(self, projName):
58 From a project's name, returns the project's details
60 todoList = [list for list in self.get_projects() if list["name"] == projName]
61 assert len(todoList) == 1, "Wrong number of lists found for %s, in %r" % (projName, todoList)
64 def get_locations(self):
65 rsp = self._rtm.locations.getList()
66 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
71 ("longitude", t.longitude),
72 ("latitude", t.latitude),
73 ("address", t.address),
75 for t in rsp.locations
79 def get_tasks_with_details(self, projId):
80 for realProjId, taskSeries in self._get_taskseries(projId):
81 for task in self._get_tasks(taskSeries):
82 taskId = self._pack_ids(realProjId, taskSeries.id, task.id)
83 priority = task.priority if task.priority != "N" else ""
87 "name": taskSeries.name,
88 "url": fix_url(taskSeries.url),
89 "locationId": taskSeries.location_id,
91 "isCompleted": len(task.completed) != 0,
92 "completedDate": task.completed,
94 "estimate": task.estimate,
95 "notes": list(self._get_notes(taskSeries.notes)),
98 def get_task_details(self, taskId):
99 projId, seriesId, taskId = self._unpack_ids(taskId)
100 for realProjId, taskSeries in self._get_taskseries(projId):
101 assert projId == realProjId, "%s != %s, looks like we let leak a metalist id when packing a task id" % (projId, realProjId)
102 curSeriesId = taskSeries.id
103 if seriesId != curSeriesId:
105 for task in self._get_tasks(taskSeries):
107 if task.id != curTaskId:
111 "projId": realProjId,
112 "name": taskSeries.name,
113 "url": fix_url(taskSeries.url),
114 "locationId": taskSeries.location_id,
116 "isCompleted": task.completed,
117 "completedDate": task.completed,
118 "priority": task.priority,
119 "estimate": task.estimate,
120 "notes": list(self._get_notes(taskSeries.notes)),
124 def add_task(self, projId, taskName):
125 rsp = self._rtm.tasks.add(
126 timeline=self._timeline,
130 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
131 seriesId = rsp.list.taskseries.id
132 taskId = rsp.list.taskseries.task.id
133 name = rsp.list.taskseries.name
135 return self._pack_ids(projId, seriesId, taskId)
137 def set_project(self, taskId, newProjId):
138 projId, seriesId, taskId = self._unpack_ids(taskId)
139 rsp = self._rtm.tasks.moveTo(
140 timeline=self._timeline,
142 to_list_id=newProjId,
143 taskseries_id=seriesId,
146 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
148 def set_name(self, taskId, name):
149 projId, seriesId, taskId = self._unpack_ids(taskId)
150 rsp = self._rtm.tasks.setName(
151 timeline=self._timeline,
153 taskseries_id=seriesId,
157 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
159 def set_duedate(self, taskId, dueDate):
160 projId, seriesId, taskId = self._unpack_ids(taskId)
161 rsp = self._rtm.tasks.setDueDate(
162 timeline=self._timeline,
164 taskseries_id=seriesId,
169 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
171 def set_priority(self, taskId, priority):
175 priority = str(priority)
176 projId, seriesId, taskId = self._unpack_ids(taskId)
178 rsp = self._rtm.tasks.setPriority(
179 timeline=self._timeline,
181 taskseries_id=seriesId,
185 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
187 def complete_task(self, taskId):
188 projId, seriesId, taskId = self._unpack_ids(taskId)
190 rsp = self._rtm.tasks.complete(
191 timeline=self._timeline,
193 taskseries_id=seriesId,
196 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
201 >>> RtMilkManager._pack_ids(123, 456)
204 return "-".join((str(id) for id in ids))
207 def _unpack_ids(ids):
209 >>> RtMilkManager._unpack_ids("123-456")
212 return ids.split("-")
214 def _get_taskseries(self, projId):
215 rsp = self._rtm.tasks.getList(
218 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
219 # @note Meta-projects return lists for each project (I think)
220 rspTasksList = rsp.tasks.list
222 if not isinstance(rspTasksList, list):
223 rspTasksList = (rspTasksList, )
225 for something in rspTasksList:
226 realProjId = something.id
229 except AttributeError:
232 if isinstance(something.taskseries, list):
233 somethingsTaskseries = something.taskseries
235 somethingsTaskseries = (something.taskseries, )
237 for taskSeries in somethingsTaskseries:
238 yield realProjId, taskSeries
240 def _get_tasks(self, taskSeries):
241 if isinstance(taskSeries.task, list):
242 tasks = taskSeries.task
244 tasks = (taskSeries.task, )
248 def _get_notes(self, notes):
251 elif isinstance(notes.note, list):
254 notes = (notes.note, )
259 body = getattr(note, "$t")
266 def _populate_projects(self):
267 rsp = self._rtm.lists.getList()
268 assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
274 ("isVisible", not int(t.archived)),
275 ("isMeta", not not int(t.smart)),
277 for t in rsp.lists.list