24
from GTG.backends.genericbackend import GenericBackend
25
from GTG.backends.backendsignals import BackendSignals
24
from GTG.backends.genericbackend import GenericBackend
25
from GTG.backends.backendsignals import BackendSignals
26
26
from GTG.backends.periodicimportbackend import PeriodicImportBackend
27
from GTG.backends.syncengine import SyncEngine, SyncMeme
28
from GTG.tools.logger import Log
27
from GTG.backends.syncengine import SyncEngine, SyncMeme
28
from GTG.tools.logger import Log
30
from GTG.core.task import Task
30
from GTG.core.task import Task
31
31
from suds.client import Client
45
45
GenericBackend.BACKEND_AUTHORS: ["Luca Invernizzi", "Alayn Gortazar"],
46
46
GenericBackend.BACKEND_TYPE: GenericBackend.TYPE_READONLY,
47
47
GenericBackend.BACKEND_DESCRIPTION:
48
_("This synchronization service lets you import the issues found"
49
" on Mantis using a prestablished filter called 'gtg'."
50
" As the issue state changes in Mantis, the GTG task is "
52
"Please note that this is a read only synchronization service,"
53
" which means that if you open one of the imported tasks and "
54
" change one of the:\n"
58
"Your changes <b>will</b> be reverted when the associated"
59
" issue is modified. Apart from those, you are free to set "
60
" any other field (start/due dates, subtasks...): your "
61
" changes will be preserved. This is useful to add "
62
" personal annotations to issue"),
48
_("This synchronization service lets you import the issues found"
49
" on Mantis using a prestablished filter called 'gtg'."
50
" As the issue state changes in Mantis, the GTG task is "
52
"Please note that this is a read only synchronization service,"
53
" which means that if you open one of the imported tasks and "
54
" change one of the:\n"
58
"Your changes <b>will</b> be reverted when the associated"
59
" issue is modified. Apart from those, you are free to set "
60
" any other field (start/due dates, subtasks...): your "
61
" changes will be preserved. This is useful to add "
62
" personal annotations to issue"),
66
65
_static_parameters = {
77
76
GenericBackend.PARAM_TYPE: GenericBackend.TYPE_STRING,
78
77
GenericBackend.PARAM_DEFAULT_VALUE: 'http://example.com/mantis',
80
79
"tag-with-project-name": {
81
80
GenericBackend.PARAM_TYPE: GenericBackend.TYPE_BOOL,
82
81
GenericBackend.PARAM_DEFAULT_VALUE: True},
85
84
def __init__(self, parameters):
88
87
Re-loads the saved state of the synchronization
90
89
super(Backend, self).__init__(parameters)
91
#loading the saved state of the synchronization, if any
90
# loading the saved state of the synchronization, if any
92
91
self.data_path = os.path.join('backends/mantis/',
93
92
"sync_engine-" + self.get_id())
94
93
self.sync_engine = self._load_pickled_file(self.data_path,
99
98
self._store_pickled_file(self.data_path, self.sync_engine)
101
100
def do_periodic_import(self):
102
#Establishing connection
101
# Establishing connection
104
103
self.cancellation_point()
105
client = Client('%s/api/soap/mantisconnect.php?wsdl' % \
106
(self._parameters['service-url']))
104
client = Client('%s/api/soap/mantisconnect.php?wsdl' %
105
(self._parameters['service-url']))
108
self.quit(disable = True)
107
self.quit(disable=True)
109
108
BackendSignals().backend_failed(self.get_id(),
110
BackendSignals.ERRNO_AUTHENTICATION)
109
BackendSignals.ERRNO_AUTHENTICATION
114
113
projects = client.service.mc_projects_get_user_accessible(
115
self._parameters['username'],
116
self._parameters['password'])
114
self._parameters['username'],
115
self._parameters['password'])
117
116
filters = client.service.mc_filter_get(self._parameters['username'],
118
self._parameters['password'], 0)
117
self._parameters['password'], 0)
119
# Fetching the issues
121
120
self.cancellation_point()
123
122
for filt in filters:
124
123
if filt['name'] == 'gtg':
125
124
for project in projects:
126
125
my_issues = client.service.mc_filter_get_issues(
127
self._parameters['username'],
128
self._parameters['password'],
126
self._parameters['username'],
127
self._parameters['password'],
131
130
for issue in my_issues:
132
131
self.cancellation_point()
133
132
self._process_mantis_issue(issue)
135
134
new_issue_list = [str(issue['id']) for issue in my_issues]
136
135
for issue_link in set(last_issue_list).difference(set(new_issue_list)):
137
136
self.cancellation_point()
138
#we make sure that the other backends are not modifying the task
137
# we make sure that the other backends are not modifying the task
140
139
with self.datastore.get_backend_mutex():
141
140
tid = self.sync_engine.get_local_id(issue_link)
142
141
self.datastore.request_task_deletion(tid)
144
self.sync_engine.break_relationship(remote_id = issue_link)
143
self.sync_engine.break_relationship(remote_id=issue_link)
159
158
@param note: a mantis issue
160
has_task = self.datastore.has_task
161
161
action, tid = self.sync_engine.analyze_remote_id(str(issue['id']),
162
self.datastore.has_task, lambda b: True)
163
164
Log.debug("processing mantis (%s)" % (action))
168
169
issue_dic = self._prefetch_issue_data(issue)
169
#for the rest of the function, no access to issue must be made, so
170
# for the rest of the function, no access to issue must be made, so
170
171
# that the time of blocking inside the with statements is short.
171
#To be sure of that, set issue to None
172
# To be sure of that, set issue to None
174
175
with self.datastore.get_backend_mutex():
176
177
tid = str(uuid.uuid4())
177
178
task = self.datastore.task_factory(tid)
178
179
self._populate_task(task, issue_dic)
179
self.sync_engine.record_relationship(local_id = tid,
180
remote_id = str(issue_dic['number']),
183
issue_dic['modified'],
180
self.sync_engine.record_relationship(local_id=tid,
182
issue_dic['number']),
185
issue_dic['modified'],
185
187
self.datastore.push_task(task)
187
189
elif action == SyncEngine.UPDATE:
188
190
task = self.datastore.get_task(tid)
189
191
self._populate_task(task, issue_dic)
190
192
meme = self.sync_engine.get_meme_from_remote_id(
192
194
meme.set_local_last_modified(task.get_modified())
193
195
meme.set_remote_last_modified(issue_dic['modified'])
194
196
self.save_state()
202
204
@returns dict: a dictionary containing the relevant issue attributes
204
206
issue_dic = {'title': mantis_issue['summary'],
205
'text': mantis_issue['description'],
206
'reporter': mantis_issue['reporter'].name,
207
'modified': mantis_issue['last_updated'],
208
'project': mantis_issue['project'].name,
209
'status': mantis_issue['status'].name,
210
'completed': (mantis_issue['status'].id >= 80),
211
'number': str(mantis_issue['id'])}
207
'text': mantis_issue['description'],
208
'reporter': mantis_issue['reporter'].name,
209
'modified': mantis_issue['last_updated'],
210
'project': mantis_issue['project'].name,
211
'status': mantis_issue['status'].name,
212
'completed': (mantis_issue['status'].id >= 80),
213
'number': str(mantis_issue['id'])}
214
216
issue_dic['assigned'] = mantis_issue['handler'].name == \
215
self._parameters['username']
217
self._parameters['username']
216
218
except AttributeError:
217
219
issue_dic['assigned'] = False
255
257
text = _("Reported by: ") + issue_dic["reporter"] + '\n'
256
258
text += _("Link to issue: ") + \
257
self._parameters['service-url'] + '/view.php?id=%s' % \
258
(issue_dic["number"]) + '\n'
259
self._parameters['service-url'] + '/view.php?id=%s' % \
260
(issue_dic["number"]) + '\n'
259
261
text += '\n' + issue_dic["text"]