~taskcoach-developers/taskcoach/trunk

« back to all changes in this revision

Viewing changes to taskcoachlib/persistence/todotxt/reader.py

  • Committer: fraca7
  • Date: 2014-03-29 17:17:22 UTC
  • Revision ID: svn-v4:1bde7e76-c350-445e-a5b0-d0792d1e7eef:trunk/taskcoach:6450
Merge with release 1.3 branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
    def __init__(self, taskList, categoryList):
26
26
        self.__taskList = taskList
27
27
        self.__tasksBySubject = self.__createSubjectCache(taskList)
 
28
        self.__tasksById = self.__createIdCache(taskList)
28
29
        self.__categoryList = categoryList
29
30
        self.__categoriesBySubject = self.__createSubjectCache(categoryList)
 
31
        self.__version = 0
30
32
 
31
33
    def read(self, filename):
32
34
        metaName = filename + '-meta'
35
37
            todoTxtRE = self.compileTodoTxtRE()
36
38
            keyValueRE = self.compileKeyValueRE()
37
39
            with codecs.open(metaName, 'r', 'utf-8') as fp:
 
40
                match = re.match(r'VERSION: (\d+)', fp.readline().strip())
 
41
                if match:
 
42
                    self.__version = int(match.group(1))
 
43
                else:
 
44
                    fp.seek(0)
 
45
 
38
46
                for line in fp:
39
47
                    line = line.strip()
40
48
                    try:
41
 
                        subjects, priority, plannedStartDateTime, completionDateTime, dueDateTime, categories = \
 
49
                        taskId, subjects, priority, plannedStartDateTime, completionDateTime, dueDateTime, categories = \
42
50
                          self.__processLine(line, todoTxtRE, keyValueRE, date.Now, None)
43
51
                    except:
44
52
                        pass # Err
45
 
                    metaLines['->'.join(subjects)] = line
 
53
                    metaLines['->'.join(subjects) if self.__version == 0 else taskId] = line
46
54
        with codecs.open(filename, 'r', 'utf-8') as fp:
47
55
            self.readFile(fp, metaLines=metaLines)
48
56
    
60
68
        # defined by the todo.txt format  at
61
69
        # https://github.com/ginatrapani/todo.txt-cli/wiki/The-Todo.txt-Format
62
70
        dueDateTime = date.DateTime()
 
71
        taskId = None
63
72
        for key, value in re.findall(keyValueRE, line):
64
73
            if key == 'due':
65
74
                dueDateTime = self.dateTime(value)
 
75
            elif key == 'tcid':
 
76
                taskId = value
66
77
        line = re.sub(keyValueRE, '', line) # Remove all key:value pairs
67
78
        
68
79
        # Now, process the "official" todo.txt format using a RE that should 
77
88
        recursiveSubject = match.group('subject')
78
89
 
79
90
        subjects = recursiveSubject.split('->')
80
 
        return subjects, priority, plannedStartDateTime, completionDateTime, dueDateTime, categories
 
91
        return taskId, subjects, priority, plannedStartDateTime, completionDateTime, dueDateTime, categories
81
92
 
82
93
    def processLine(self, line, todoTxtRE, keyValueRE, now, event, metaLines):
83
 
        subjects, priority, plannedStartDateTime, completionDateTime, dueDateTime, categories = \
 
94
        taskId, subjects, priority, plannedStartDateTime, completionDateTime, dueDateTime, categories = \
84
95
          self.__processLine(line, todoTxtRE, keyValueRE, now, event)
85
96
 
86
 
        if metaLines and metaLines.get('->'.join(subjects), None) == line:
 
97
        if (self.__version == 0 and metaLines and metaLines.get('->'.join(subjects), None) == line) or \
 
98
          (self.__version == 1 and metaLines and taskId and metaLines.get(taskId, None) == line):
87
99
            # Not modified. Don't read it or we'll overwrite local changes
88
100
            # in case we're importing just before saving...
89
101
            return
90
102
 
91
 
        newTask = None
92
 
        for subject in subjects:
93
 
            newTask = self.findOrCreateTask(subject.strip(), newTask, event)
94
 
        
 
103
        if taskId is not None and taskId not in self.__tasksById:
 
104
            return # Deleted on desktop, changed on device. Keep deleted.
 
105
 
 
106
        if self.__version == 0:
 
107
            newTask = None
 
108
            for subject in subjects:
 
109
                newTask = self.findOrCreateTask(subject.strip(), newTask, event)
 
110
        else:
 
111
            newTask = None if taskId is None else self.__tasksById.get(taskId, None)
 
112
            if newTask is None:
 
113
                newTask = task.Task(subject=subjects[-1])
 
114
                self.__taskList.append(newTask)
 
115
            else:
 
116
                newTask.setSubject(subjects[-1])
 
117
 
95
118
        newTask.setPriority(priority)
96
119
        newTask.setPlannedStartDateTime(plannedStartDateTime)
97
120
        newTask.setCompletionDateTime(completionDateTime)
177
200
        
178
201
    @staticmethod
179
202
    def compileKeyValueRE():
180
 
        return re.compile(' (?P<key>\S+):(?P<value>\S+)')
 
203
        # The key is non-greedy because IDs may contain ':'
 
204
        return re.compile(' (?P<key>\S+?):(?P<value>\S+)')
181
205
    
182
206
    @staticmethod
183
207
    def __createSubjectCache(itemContainer):
186
210
            cache[(item.subject(), item.parent())] = item
187
211
        return cache
188
212
 
189
 
        
 
213
    @staticmethod
 
214
    def __createIdCache(itemContainer):
 
215
        cache = dict()
 
216
        for item in itemContainer:
 
217
            cache[item.id()] = item
 
218
        return cache