~saurabhanandiit/gtg/exportFixed

« back to all changes in this revision

Viewing changes to GTG/plugins/rtm_sync/rtmTask.py

Merge of my work on liblarch newbase and all the backends ported to liblarch
(which mainly means porting the datastore).
One failing test, will check it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# Copyright (c) 2009 - Luca Invernizzi <invernizzi.l@gmail.com>
3
 
#
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
7
 
# version.
8
 
#
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
12
 
# details.
13
 
#
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/>.
16
 
 
17
 
import datetime
18
 
from dateutil.tz import tzutc, tzlocal
19
 
 
20
 
from GTG.tools.logger import Log
21
 
from GTG.plugins.rtm_sync.genericTask import GenericTask
22
 
 
23
 
 
24
 
class RtmTask(GenericTask):
25
 
    
26
 
    def __init__(self, task, list_id, taskseries_id, rtm, timeline, proxy):
27
 
        super(RtmTask, self).__init__(proxy)
28
 
        self.rtm = rtm
29
 
        self.timeline = timeline
30
 
        self.task = task
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")
37
 
 
38
 
    def _get_title(self):
39
 
        if hasattr(self.task,"name"):
40
 
            return self.task.name
41
 
        else:
42
 
            self.__log("rtm task has no title: " + str(self.task))
43
 
            return ""
44
 
 
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, \
49
 
                        task_id=self.id, \
50
 
                        name = title)
51
 
 
52
 
    def _get_id(self):
53
 
        self.__log("getting the task id")
54
 
        return self.__get_rtm_task_attribute("id")
55
 
 
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:
61
 
                self.recurring = True
62
 
                return getattr(self.task.task[len(self.task.task) - 1], attr)
63
 
            else:
64
 
                return getattr(self.task.task, attr)
65
 
        else:
66
 
            if type(self.task) == list:
67
 
                return getattr(self.task[len(self.task) - 1], attr)
68
 
            else:
69
 
                return getattr(self.task, attr)
70
 
 
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 == ""]
75
 
 
76
 
    def _set_status(self, gtg_status):
77
 
        status = self.get_proxy()._gtg_to_rtm_status[gtg_status]
78
 
        if status == True:
79
 
            self.rtm.tasks.uncomplete(timeline=self.timeline, \
80
 
                                      list_id = self.list_id,\
81
 
                                      taskseries_id = self.taskseries_id, \
82
 
                                      task_id = self.id)
83
 
        else:
84
 
            self.rtm.tasks.complete(timeline=self.timeline, \
85
 
                                      list_id = self.list_id,\
86
 
                                      taskseries_id = self.taskseries_id, \
87
 
                                      task_id = self.id)
88
 
 
89
 
    def _get_tags(self):
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
95
 
            else:
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, \
100
 
                       self.task.tags.list)
101
 
        return []
102
 
 
103
 
    def _set_tags(self, tags):
104
 
        #remove the @ at the beginning
105
 
        tags_purified = []
106
 
        for tag in tags:
107
 
            if tag[0] == '@':
108
 
                tag = tag[1:]
109
 
            tags_purified.append(tag.lower())
110
 
 
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:
116
 
            return
117
 
 
118
 
        #sync
119
 
        if len(tags_purified) > 0:
120
 
            tagstxt = reduce(lambda x,y: x + ", " + y, tags_purified)
121
 
        else:
122
 
            tagstxt = ""
123
 
        self.rtm.tasks.setTags(timeline=self.timeline, \
124
 
                        list_id =self.list_id, \
125
 
                        taskseries_id=self.taskseries_id, \
126
 
                        task_id=self.id, \
127
 
                        tags=tagstxt)
128
 
 
129
 
    def _get_text(self):
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))
136
 
            else:
137
 
                text = getattr(self.task.notes.note, '$t')
138
 
        else:
139
 
            text = ""
140
 
        #adding back the tags (subtasks are added automatically)
141
 
        tags = self.tags
142
 
        if len(tags) > 0:
143
 
            tagstxt = "@" + reduce(lambda x,y: x + ", " + "@" + y, self.tags) + "\n"
144
 
        else:
145
 
            tagstxt = ""
146
 
        return tagstxt + text.strip()
147
 
 
148
 
    def _set_text(self, text):
149
 
        #delete old notes
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)
154
 
            else:
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)
158
 
        #add a new one
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"?
163
 
        if text == "":
164
 
            return
165
 
        self.rtm.tasksNotes.add(timeline=self.timeline, \
166
 
                                list_id = self.list_id,\
167
 
                                taskseries_id = self.taskseries_id, \
168
 
                                task_id = self.id, \
169
 
                                note_title = "",\
170
 
                                note_text = text)
171
 
 
172
 
    def _get_due_date(self):
173
 
        if hasattr(self.task,'task'):
174
 
            if type(self.task.task) != list:
175
 
                task = self.task.task
176
 
            else:
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()
181
 
        return None
182
 
 
183
 
    def _set_due_date(self, due):
184
 
        if due != None:
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, \
189
 
                                      task_id = self.id, \
190
 
                                      parse = 1, \
191
 
                                      due=due_string)
192
 
        else:
193
 
            self.rtm.tasks.setDueDate(timeline=self.timeline, \
194
 
                                      list_id = self.list_id,\
195
 
                                      taskseries_id = self.taskseries_id, \
196
 
                                      task_id = self.id)
197
 
 
198
 
    def _get_modified(self):
199
 
        if not hasattr(self.task, 'modified') or self.task.modified == "":
200
 
            return None
201
 
        modified = self.__time_rtm_to_datetime(self.task.modified)
202
 
        if self.recurring == False:
203
 
            return modified
204
 
        else:
205
 
            now = datetime.datetime.now()
206
 
            this_morning =datetime.datetime(year = now.year,\
207
 
                                        month = now.month,\
208
 
                                        day = now.day)
209
 
            if modified > this_morning:
210
 
                return modified
211
 
            else:
212
 
                return this_morning
213
 
 
214
 
    def delete(self):
215
 
        self.rtm.tasks.delete(timeline = self.timeline, \
216
 
                              list_id = self.list_id, \
217
 
                              taskseries_id = self.taskseries_id, \
218
 
                              task_id = self.id)
219
 
 
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)
225
 
 
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)
230
 
 
231
 
    def __time_rtm_to_datetime(self, string):
232
 
        string = string.split('.')[0].split('Z')[0]
233
 
        dt = datetime.datetime.strptime(string.split(".")[0], \
234
 
                                          "%Y-%m-%dT%H:%M:%S")
235
 
        return self.__tz_utc_to_local(dt)
236
 
        
237
 
 
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)
242
 
 
243
 
 
244
 
    def __time_datetime_to_rtm(self, timeobject):
245
 
        if timeobject == None:
246
 
            return ""
247
 
        timeobject = self.__tz_local_to_utc(timeobject)
248
 
        return timeobject.strftime("%Y-%m-%dT%H:%M:%S")
249
 
 
250
 
    def __time_date_to_rtm(self, timeobject):
251
 
        if timeobject == None:
252
 
            return ""
253
 
        #WARNING: no timezone? seems to break the symmetry.
254
 
        return timeobject.strftime("%Y-%m-%d")
255
 
 
256
 
    def __log(self, message):
257
 
        Log.debug(message)
258