import warnings
import contextlib
+import datetime
import gobject
import gtk
+import toolbox
+
@contextlib.contextmanager
def gtk_lock():
self.__popupWindow.set_transient_for(parent)
self.__popupWindow.set_modal(True)
+ self.callback = lambda: None
+
def get_date(self):
year, month, day = self.__calendar.get_date()
month += 1 # Seems to be 0 indexed
- return year, month, day
+ return datetime.date(year, month, day)
def run(self):
self.__popupWindow.show_all()
originalProjectId = taskDetails["projId"]
originalProjectName = todoManager.get_project(originalProjectId)["name"]
originalName = taskDetails["name"]
- try:
- originalPriority = int(taskDetails["priority"])
- except ValueError:
- originalPriority = 0
- originalDue = taskDetails["due"]
+ originalPriority = str(taskDetails["priority"].get_nothrow(0))
+ if taskDetails["dueDate"].is_good():
+ originalDue = taskDetails["dueDate"].get().strftime("%Y-%m-%d %H:%M:%S")
+ else:
+ originalDue = ""
self._dialog.set_default_response(gtk.RESPONSE_OK)
self._taskName.set_text(originalName)
self._set_active_proj(originalProjectName)
- self._priorityChoiceCombo.set_active(originalPriority)
+ self._priorityChoiceCombo.set_active(int(originalPriority))
self._dueDateDisplay.set_text(originalDue)
try:
todoManager.set_name(taskId, newName)
print "NAME CHANGE"
if isPriorityDifferent:
- todoManager.set_priority(taskId, newPriority)
+ try:
+ priority = toolbox.Optional(int(newPriority))
+ except ValueError:
+ priority = toolbox.Optional()
+ todoManager.set_priority(taskId, priority)
print "PRIO CHANGE"
if isDueDifferent:
- todoManager.set_duedate(taskId, newDueDate)
+ if newDueDate:
+ due = datetime.datetime.strptime(newDueDate, "%Y-%m-%d %H:%M:%S")
+ due = toolbox.Optional(due)
+ else:
+ due = toolbox.Optional()
+
+ todoManager.set_duedate(taskId, due)
print "DUE CHANGE"
return {
else:
return str(index)
- def _get_date(self):
- # @bug The date is not used in a consistent manner causing ... issues
- dateParts = self._popupCalendar.get_date()
- dateParts = (str(part) for part in dateParts)
- return "-".join(dateParts)
-
def _update_duedate(self):
- self._dueDateDisplay.set_text(self._get_date())
+ date = self._popupCalendar.get_date()
+ time = datetime.time()
+ dueDate = datetime.datetime.combine(date, time)
+
+ formttedDate = dueDate.strftime("%Y-%m-%d %H:%M:%S")
+ self._dueDateDisplay.set_text(formttedDate)
def _on_name_paste(self, *args):
clipboard = gtk.clipboard_get()
Wrapper for Remember The Milk API
"""
+import datetime
+import toolbox
import rtm_api
for realProjId, taskSeries in self._get_taskseries(projId):
for task in self._get_tasks(taskSeries):
taskId = self._pack_ids(realProjId, taskSeries.id, task.id)
- priority = task.priority if task.priority != "N" else ""
- yield {
+ rawTaskDetails = {
"id": taskId,
"projId": projId,
"name": taskSeries.name,
- "url": fix_url(taskSeries.url),
+ "url": taskSeries.url,
"locationId": taskSeries.location_id,
"dueDate": task.due,
- "isCompleted": len(task.completed) != 0,
- "completedDate": task.completed,
- "priority": priority,
- "estimate": task.estimate,
- "notes": list(self._get_notes(taskSeries.notes)),
- }
-
- def get_task_details(self, taskId):
- projId, seriesId, taskId = self._unpack_ids(taskId)
- for realProjId, taskSeries in self._get_taskseries(projId):
- assert projId == realProjId, "%s != %s, looks like we let leak a metalist id when packing a task id" % (projId, realProjId)
- curSeriesId = taskSeries.id
- if seriesId != curSeriesId:
- continue
- for task in self._get_tasks(taskSeries):
- curTaskId = task.id
- if task.id != curTaskId:
- continue
- return {
- "id": taskId,
- "projId": realProjId,
- "name": taskSeries.name,
- "url": fix_url(taskSeries.url),
- "locationId": taskSeries.location_id,
- "due": task.due,
"isCompleted": task.completed,
"completedDate": task.completed,
"priority": task.priority,
"estimate": task.estimate,
- "notes": list(self._get_notes(taskSeries.notes)),
+ "notes": list(self._get_notes(taskId, taskSeries.notes)),
}
+ taskDetails = self._parse_task_details(rawTaskDetails)
+ yield taskDetails
+
+ def get_task_details(self, taskId):
+ projId, rtmSeriesId, rtmTaskId = self._unpack_ids(taskId)
+ for task in self.get_tasks_with_details(projId):
+ if task["id"] == taskId:
+ return task
return {}
def add_task(self, projId, taskName):
assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
def set_duedate(self, taskId, dueDate):
+ assert isinstance(dueDate, toolbox.Optional), (
+ "Date being set too definitively: %r" % dueDate
+ )
+
projId, seriesId, taskId = self._unpack_ids(taskId)
rsp = self._rtm.tasks.setDueDate(
timeline=self._timeline,
assert rsp.stat == "ok", "Bad response: %r" % (rsp, )
def set_priority(self, taskId, priority):
- if priority is None:
- priority = "N"
- else:
- priority = str(priority)
+ assert isinstance(priority, toolbox.Optional), (
+ "Priority being set too definitively: %r" % priority
+ )
+ priority = str(priority.get_nothrow("N"))
projId, seriesId, taskId = self._unpack_ids(taskId)
rsp = self._rtm.tasks.setPriority(
for task in tasks:
yield task
- def _get_notes(self, notes):
+ def _parse_task_details(self, rawTaskDetails):
+ taskDetails = {}
+ taskDetails["id"] = rawTaskDetails["id"]
+ taskDetails["projId"] = rawTaskDetails["projId"]
+ taskDetails["name"] = rawTaskDetails["name"]
+ taskDetails["url"] = fix_url(rawTaskDetails["url"])
+
+ rawLocationId = rawTaskDetails["locationId"]
+ if rawLocationId:
+ locationId = toolbox.Optional(rawLocationId)
+ else:
+ locationId = toolbox.Optional()
+ taskDetails["locationId"] = locationId
+
+ if rawTaskDetails["dueDate"]:
+ dueDate = datetime.datetime.strptime(
+ rawTaskDetails["dueDate"],
+ "%Y-%m-%dT%H:%M:%SZ",
+ )
+ dueDate = toolbox.Optional(dueDate)
+ else:
+ dueDate = toolbox.Optional()
+ taskDetails["dueDate"] = dueDate
+
+ taskDetails["isCompleted"] = len(rawTaskDetails["isCompleted"]) != 0
+
+ if rawTaskDetails["completedDate"]:
+ completedDate = datetime.datetime.strptime(
+ rawTaskDetails["completedDate"],
+ "%Y-%m-%dT%H:%M:%SZ",
+ )
+ completedDate = toolbox.Optional(completedDate)
+ else:
+ completedDate = toolbox.Optional()
+ taskDetails["completedDate"] = completedDate
+
+ try:
+ priority = toolbox.Optional(int(rawTaskDetails["priority"]))
+ except ValueError:
+ priority = toolbox.Optional()
+ taskDetails["priority"] = priority
+
+ if rawTaskDetails["estimate"]:
+ estimate = rawTaskDetails["estimate"]
+ estimate = toolbox.Optional(estimate)
+ else:
+ estimate = toolbox.Optional()
+ taskDetails["estimate"] = estimate
+
+ taskDetails["notes"] = rawTaskDetails["notes"]
+
+ rawKeys = list(rawTaskDetails.iterkeys())
+ rawKeys.sort()
+ parsedKeys = list(taskDetails.iterkeys())
+ parsedKeys.sort()
+ assert rawKeys == parsedKeys, "Missing some, %r != %r" % (rawKeys, parsedKeys)
+
+ return taskDetails
+
+ def _get_notes(self, taskId, notes):
if not notes:
return
elif isinstance(notes.note, list):
else:
notes = (notes.note, )
+ projId, rtmSeriesId, rtmTaskId = self._unpack_ids(taskId)
+
for note in notes:
- id = note.id
+ noteId = self._pack_ids(projId, rtmSeriesId, rtmTaskId, note.id)
title = note.title
body = getattr(note, "$t")
yield {
- "id": id,
+ "id": noteId,
"title": title,
"body": body,
}
import datetime
+class Optional(object):
+ """
+ Taglines:
+ Even you don't have to worry about knowing when to perform None checks
+ When the NULL object pattern just isn't good enough
+
+ >>> a = Optional()
+ >>> a.is_good()
+ False
+ >>> a.get_nothrow("Blacksheep")
+ 'Blacksheep'
+ >>> a.set("Lamb")
+ >>> a.is_good()
+ True
+ >>> a.get_nothrow("Blacksheep"), a.get(), a()
+ ('Lamb', 'Lamb', 'Lamb')
+ >>> a.clear()
+ >>> a.is_good()
+ False
+ >>> a.get_nothrow("Blacksheep")
+ 'Blacksheep'
+ >>>
+ >>> b = Optional("Lamb")
+ >>> a.set("Lamb")
+ >>> a.is_good()
+ True
+ >>> a.get_nothrow("Blacksheep"), a.get(), a()
+ ('Lamb', 'Lamb', 'Lamb')
+ >>> a.clear()
+ >>> a.is_good()
+ False
+ >>> a.get_nothrow("Blacksheep")
+ 'Blacksheep'
+ """
+
+ class NonExistent(object):
+ pass
+
+ def __init__(self, value = NonExistent):
+ self._value = value
+
+ def set(self, value):
+ self._value = value
+
+ def clear(self):
+ self._value = self.NonExistent
+
+ def is_good(self):
+ return self._value is not self.NonExistent
+
+ def get_nothrow(self, default = None):
+ return self._value if self.is_good() else default
+
+ def get(self):
+ if not self.is_good():
+ raise ReferenceError("Optional not initialized")
+ return self._value
+
+ def __call__(self):
+ # Implemented to imitate weakref
+ return self.get()
+
+ def __str__(self):
+ return str(self.get_nothrow(""))
+
+ def __repr__(self):
+ return "<Optional at %x; to %r>" % (
+ id(self), self.get_nothrow("Nothing")
+ )
+
+
def open_anything(source, alternative=None):
"""URI, filename, or string --> stream