29
32
from GTG.core import CoreConfig
30
33
from GTG.tools import cleanxml, taskxml
35
from GTG.tools.logger import Log
35
39
class Backend(GenericBackend):
41
Localfile backend, which stores your tasks in a XML file in the standard
42
XDG_DATA_DIR/gtg folder (the path is configurable).
43
An instance of this class is used as the default backend for GTG.
44
This backend loads all the tasks stored in the localfile after it's enabled,
45
and from that point on just writes the changes to the file: it does not
46
listen for eventual file changes
38
50
DEFAULT_PATH = CoreConfig().get_data_dir() #default path for filenames
41
#Description of the backend (mainly it's data we show the user, only the
42
# name is used internally. Please note that BACKEND_NAME and
43
# BACKEND_ICON_NAME should *not* be translated.
53
#General description of the backend: these are used to show a description of
54
# the backend to the user when s/he is considering adding it.
55
# BACKEND_NAME is the name of the backend used internally (it must be
57
#Please note that BACKEND_NAME and BACKEND_ICON_NAME should *not* be
44
59
_general_description = { \
45
60
GenericBackend.BACKEND_NAME: "backend_localfile", \
46
61
GenericBackend.BACKEND_HUMAN_NAME: _("Local File"), \
53
68
"for GTG to save your tasks."),\
56
#parameters to configure a new backend of this type.
57
#NOTE: should we always give back a different default filename? it can be
58
# done, but I'd like to keep this backend simple, so that it can be
59
# used as example (invernizzi)
71
#These are the parameters to configure a new backend of this type. A
72
# parameter has a name, a type and a default value.
73
# Here, we define a parameter "path", which is a string, and has a default
74
# value as a random file in the default path
75
#NOTE: to keep this simple, the filename default path is the same until GTG
76
# is restarted. I consider this a minor annoyance, and we can avoid
77
# coding the change of the path each time a backend is
78
# created (invernizzi)
60
79
_static_parameters = { \
62
81
GenericBackend.PARAM_TYPE: GenericBackend.TYPE_STRING, \
64
83
os.path.join(DEFAULT_PATH, "gtg_tasks-%s.xml" %(uuid.uuid4()))
67
def _get_default_filename_path(self, filename = None):
69
Generates a default path with a random filename
70
@param filename: specify a filename
73
filename = "gtg_tasks-%s.xml" % (uuid.uuid4())
74
return os.path.join(self.DEFAULT_PATH, filename)
76
86
def __init__(self, parameters):
78
88
Instantiates a new backend.
80
@param parameters: should match the dictionary returned in
81
get_parameters. Anyway, the backend should care if one expected
82
value is None or does not exist in the dictionary.
83
@firstrun: only needed for the default backend. It should be
84
omitted for all other backends.
90
@param parameters: A dictionary of parameters, generated from
91
_static_parameters. A few parameters are added to those, the list of
92
these is in the "DefaultBackend" class, look for the KEY_* constants.
94
The backend should take care if one expected value is None or
95
does not exist in the dictionary.
86
97
super(Backend, self).__init__(parameters)
98
self.tids = [] #we keep the list of loaded task ids here
88
99
#####RETROCOMPATIBILIY
89
#NOTE: retrocompatibility. We convert "filename" to "path"
90
# and we forget about "filename"
100
#NOTE: retrocompatibility from the 0.2 series to 0.3.
101
# We convert "filename" to "path and we forget about "filename "
91
102
if "need_conversion" in parameters:
92
103
parameters["path"] = os.path.join(self.DEFAULT_PATH, \
93
104
parameters["need_conversion"])
99
110
self._parameters["path"], "project")
101
112
def initialize(self):
113
"""This is called when a backend is enabled"""
102
114
super(Backend, self).initialize()
103
115
self.doc, self.xmlproj = cleanxml.openxmlfile( \
104
116
self._parameters["path"], "project")
106
118
def this_is_the_first_run(self, xml):
107
#Create the default tasks for the first run.
108
#We write the XML object in a file
120
Called upon the very first GTG startup.
121
This function is needed only in this backend, because it can be used as
123
The xml parameter is an object containing GTG default tasks. It will be
124
saved to a file, and the backend will be set as default.
125
@param xml: an xml object containing the default tasks.
109
127
self._parameters[self.KEY_DEFAULT_BACKEND] = True
110
128
cleanxml.savexml(self._parameters["path"], xml)
111
129
self.doc, self.xmlproj = cleanxml.openxmlfile(\
112
130
self._parameters["path"], "project")
113
self._parameters[self.KEY_DEFAULT_BACKEND] = True
115
132
def start_get_tasks(self):
117
Once this function is launched, the backend can start pushing
118
tasks to gtg parameters.
134
This function starts submitting the tasks from the XML file into GTG core.
135
It's run as a separate thread.
120
137
@return: start_get_tasks() might not return or finish
130
147
self.datastore.push_task(task)
132
149
def set_task(self, task):
135
#First, we find the existing task from the treenode
136
for node in self.xmlproj.childNodes:
137
if node.getAttribute("id") == tid:
139
t_xml = taskxml.task_to_xml(self.doc, task)
141
#We then replace the existing node
142
if existing and t_xml:
143
#We will write only if the task has changed
144
if t_xml.toxml() != existing.toxml():
145
self.xmlproj.replaceChild(t_xml, existing)
147
#If the node doesn't exist, we create it
148
# (it might not be the case in all backends
150
self.xmlproj.appendChild(t_xml)
151
This function is called from GTG core whenever a task should be
152
saved, either because it's a new one or it has been modified.
153
This function will look into the loaded XML object if the task is
154
present, and if it's not, it will create it. Then, it will save the
155
task data in the XML object.
157
@param task: the task object to save
160
#We create an XML representation of the task
161
t_xml = taskxml.task_to_xml(self.doc, task)
163
#we find if the task exists in the XML treenode.
165
for node in self.xmlproj.childNodes:
166
if node.getAttribute("id") == tid:
170
#We then replace the existing node
171
if existing and t_xml:
172
#We will write only if the task has changed
173
if t_xml.toxml() != existing.toxml():
174
self.xmlproj.replaceChild(t_xml, existing)
152
#In this particular backend, we write all the tasks
153
#This is inherent to the XML file backend
154
if modified and self._parameters["path"] and self.doc :
155
cleanxml.savexml(self._parameters["path"], self.doc)
176
#If the node doesn't exist, we create it
178
self.xmlproj.appendChild(t_xml)
181
#if the XML object has changed, we save it to file
182
if modified and self._parameters["path"] and self.doc :
183
cleanxml.savexml(self._parameters["path"], self.doc)
157
185
def remove_task(self, tid):
158
''' Completely remove the task with ID = tid '''
186
''' This function is called from GTG core whenever a task must be
187
removed from the backend. Note that the task could be not present here.
189
@param tid: the id of the task to delete
159
192
for node in self.xmlproj.childNodes:
160
193
if node.getAttribute("id") == tid:
161
195
self.xmlproj.removeChild(node)
162
196
if tid in self.tids:
163
197
self.tids.remove(tid)
164
cleanxml.savexml(self._parameters["path"], self.doc)
167
def quit(self, disable = False):
169
Called when GTG quits or disconnects the backend.
171
super(Backend, self).quit(disable)
173
def save_state(self):
174
cleanxml.savexml(self._parameters["path"], self.doc, backup=True)
176
def get_number_of_tasks(self):
178
Returns the number of tasks stored in the backend. Doesn't need to be a
179
fast function, is called just for the UI
181
return len(self.tids)
199
#We save the XML file only if it's necessary
201
cleanxml.savexml(self._parameters["path"], self.doc)
203
#NOTE: This is not used currently. Therefore, I'm disabling it (invernizzi)
204
# def get_number_of_tasks(self):
206
# Returns the number of tasks stored in the backend. Doesn't need to be a
207
# fast function, is called just for the UI
209
# return len(self.tids)