1
# -*- coding: utf-8 -*-
2
# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@gmail.com>
4
# This program is free software: you can redistribute it and/or modify it under
5
# the terms of the GNU General Public License as published by the Free Software
6
# Foundation, either version 3 of the License, or (at your option) any later
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
# You should have received a copy of the GNU General Public License along with
15
# this program. If not, see <http://www.gnu.org/licenses/>.
18
from dateutil.tz import tzutc, tzlocal
20
from GTG.tools.logger import Log
21
from GTG.plugins.rtm_sync.genericTask import GenericTask
24
class RtmTask(GenericTask):
26
def __init__(self, task, list_id, taskseries_id, rtm, timeline, proxy):
27
super(RtmTask, self).__init__(proxy)
29
self.timeline = timeline
31
self.list_id = list_id
32
self.taskseries_id = taskseries_id
33
#Checking if a task is recurring is done inside __get_rtm_task_attribute
34
# so we call that to set self.recurring correctly
35
self.recurring = False
36
self.__get_rtm_task_attribute("id")
39
if hasattr(self.task,"name"):
42
self.__log("rtm task has no title: " + str(self.task))
45
def _set_title(self, title):
46
self.rtm.tasks.setName(timeline=self.timeline, \
47
list_id =self.list_id, \
48
taskseries_id=self.taskseries_id, \
53
self.__log("getting the task id")
54
return self.__get_rtm_task_attribute("id")
56
def __get_rtm_task_attribute(self, attr):
57
if hasattr(self.task, 'task'):
58
if hasattr(self.task.task, 'list'):
59
return getattr(self.task.task.list, attr)
60
elif type(self.task.task) == list:
62
return getattr(self.task.task[len(self.task.task) - 1], attr)
64
return getattr(self.task.task, attr)
66
if type(self.task) == list:
67
return getattr(self.task[len(self.task) - 1], attr)
69
return getattr(self.task, attr)
71
def _get_status(self):
72
completed = self.__get_rtm_task_attribute("completed")
73
self.__log("getting status:" + str(completed))
74
return self.get_proxy()._rtm_to_gtg_status[completed == ""]
76
def _set_status(self, gtg_status):
77
status = self.get_proxy()._gtg_to_rtm_status[gtg_status]
79
self.rtm.tasks.uncomplete(timeline=self.timeline, \
80
list_id = self.list_id,\
81
taskseries_id = self.taskseries_id, \
84
self.rtm.tasks.complete(timeline=self.timeline, \
85
list_id = self.list_id,\
86
taskseries_id = self.taskseries_id, \
90
self.__log("getting tag list(1): " + str(self.task))
91
if hasattr(self.task,"tags") and hasattr(self.task.tags, 'tag'):
92
self.__log("getting tag list(2): " + str(self.task.tags.tag))
93
if type(self.task.tags.tag) ==list:
94
return self.task.tags.tag
96
return [self.task.tags.tag]
97
elif hasattr(self.task,"tags") and hasattr(self.task.tags, 'list'):
98
self.__log("getting tag list(2): " + str(self.task.tags.list))
99
return map(lambda x: x.tag if hasattr(x, 'tag') else None, \
103
def _set_tags(self, tags):
104
#remove the @ at the beginning
109
tags_purified.append(tag.lower())
111
#check if it's necessary to sync
112
rtm_tags_set = set(self.tags)
113
tags_purified_set = set(tags_purified)
114
if rtm_tags_set.intersection(tags_purified_set) == set() and \
115
rtm_tags_set.union(tags_purified_set) == rtm_tags_set:
119
if len(tags_purified) > 0:
120
tagstxt = reduce(lambda x,y: x + ", " + y, tags_purified)
123
self.rtm.tasks.setTags(timeline=self.timeline, \
124
list_id =self.list_id, \
125
taskseries_id=self.taskseries_id, \
130
if hasattr(self.task, 'notes') and \
131
hasattr(self.task.notes, 'note'):
132
#Rtm saves the notes text inside the member "$t". Don't ask me why.
133
if type(self.task.notes.note) == list:
134
text = "".join(map(lambda note: getattr(note, '$t') + "\n", \
135
self.task.notes.note))
137
text = getattr(self.task.notes.note, '$t')
140
#adding back the tags (subtasks are added automatically)
143
tagstxt = "@" + reduce(lambda x,y: x + ", " + "@" + y, self.tags) + "\n"
146
return tagstxt + text.strip()
148
def _set_text(self, text):
150
if hasattr(self.task, 'notes') and \
151
hasattr(self.task.notes, 'note'):
152
if type(self.task.notes.note) == list:
153
note_ids =map(lambda note: note.id, self.task.notes.note)
155
note_ids = [self.task.notes.note.id]
156
map(lambda id: self.rtm.tasksNotes.delete(timeline=self.timeline, \
157
note_id=id), note_ids)
159
#TODO: investigate what is "Note title", since there doesn't seem to
160
#be a note title in the web access.
161
#FIXME: minidom this way is ok, or do we suppose to get multiple
162
# nodes in "content"?
165
self.rtm.tasksNotes.add(timeline=self.timeline, \
166
list_id = self.list_id,\
167
taskseries_id = self.taskseries_id, \
172
def _get_due_date(self):
173
if hasattr(self.task,'task'):
174
if type(self.task.task) != list:
175
task = self.task.task
177
task = self.task.task[len(self.task.task) - 1]
178
if hasattr(task, 'due') and task.due != "":
179
to_return = self.__time_rtm_to_datetime(task.due)
180
return to_return.date()
183
def _set_due_date(self, due):
185
due_string = self.__time_date_to_rtm(due)
186
self.rtm.tasks.setDueDate(timeline=self.timeline, \
187
list_id = self.list_id,\
188
taskseries_id = self.taskseries_id, \
193
self.rtm.tasks.setDueDate(timeline=self.timeline, \
194
list_id = self.list_id,\
195
taskseries_id = self.taskseries_id, \
198
def _get_modified(self):
199
if not hasattr(self.task, 'modified') or self.task.modified == "":
201
modified = self.__time_rtm_to_datetime(self.task.modified)
202
if self.recurring == False:
205
now = datetime.datetime.now()
206
this_morning =datetime.datetime(year = now.year,\
209
if modified > this_morning:
215
self.rtm.tasks.delete(timeline = self.timeline, \
216
list_id = self.list_id, \
217
taskseries_id = self.taskseries_id, \
220
#RTM speaks utc, and accepts utc if the "parse" option is set.
221
def __tz_utc_to_local(self, dt):
222
dt = dt.replace(tzinfo = tzutc())
223
dt = dt.astimezone(tzlocal())
224
return dt.replace(tzinfo = None)
226
def __tz_local_to_utc(self, dt):
227
dt = dt.replace(tzinfo = tzlocal())
228
dt = dt.astimezone(tzutc())
229
return dt.replace(tzinfo = None)
231
def __time_rtm_to_datetime(self, string):
232
string = string.split('.')[0].split('Z')[0]
233
dt = datetime.datetime.strptime(string.split(".")[0], \
235
return self.__tz_utc_to_local(dt)
238
def __time_rtm_to_date(self, string):
239
string = string.split('.')[0].split('Z')[0]
240
dt = datetime.datetime.strptime(string.split(".")[0], "%Y-%m-%d")
241
return self.__tz_utc_to_local(dt)
244
def __time_datetime_to_rtm(self, timeobject):
245
if timeobject == None:
247
timeobject = self.__tz_local_to_utc(timeobject)
248
return timeobject.strftime("%Y-%m-%dT%H:%M:%S")
250
def __time_date_to_rtm(self, timeobject):
251
if timeobject == None:
253
#WARNING: no timezone? seems to break the symmetry.
254
return timeobject.strftime("%Y-%m-%d")
256
def __log(self, message):