1
# -*- coding: utf-8 -*-
2
# Copyright (c) 2010 - Bryce Harrington <bryce@canonical.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/>.
17
# This plugin was built for a specific purpose and is not a general-purpose or
18
# full-featured JSON importer.
20
# It imports JSON files with the following syntax.
22
# There just be a top-level key named "specs", with key names of your choosing.
23
# Each sub sub-hash must have a "details_url" or "url" key, whose values
24
# becomes part of the description of every item in the "work_items" array.
25
# There is no way to set any other description, nor it is possible to import
28
# Only items with a "status" of "todo" are imported, the rest are skipped.
30
# The software will first scan the file to find all the "assignee" values in the file
31
# And then pop-up a dialog as you to choose an username. It then imports only the TODO
32
# items for those usernames. The username is not actually imported.
37
# "details_url": "http://www.gnome.org/",
40
# "assignee": "john-doe",
41
# "description": "Do something",
60
# Choose simplejson or json by which is available
62
import simplejson as json
66
from GTG.tools.readurl import readurl
68
class pluginImportJson:
71
self.plugin_api = None
73
self.menu_item = gtk.MenuItem("Import from _JSON")
74
self.menu_item.connect('activate', self.on_import_json_activate)
76
self.tb_button = gtk.ToolButton(gtk.STOCK_INFO)
77
self.tb_button.set_label("Import from JSON")
78
self.tb_button.connect('clicked', self.on_import_json_activate)
79
self.separator = gtk.SeparatorToolItem()
83
self.json_tasks = None
85
self.dialog_select_username = None
86
self.select_username = None
89
def activate(self, plugin_api):
90
self.plugin_api = plugin_api
91
self.plugin_api.add_menu_item(self.menu_item)
92
self.plugin_api.add_toolbar_item(self.separator)
93
self.plugin_api.add_toolbar_item(self.tb_button)
95
def onTaskClosed(self, plugin_api):
98
def onTaskOpened(self, plugin_api):
101
def deactivate(self, plugin_api):
102
plugin_api.remove_menu_item(self.menu_item)
103
plugin_api.remove_toolbar_item(self.tb_button)
104
plugin_api.remove_toolbar_item(self.separator)
105
self.txtImport = None
107
def loadDialog(self):
108
self.builder = gtk.Builder()
109
self.builder.add_from_file(os.path.join(
110
os.path.dirname(os.path.abspath(__file__)) + \
113
self.dialog = self.builder.get_object("dlg_import_json")
116
self.txtImport = self.builder.get_object("txt_import")
118
self.dialog.connect("delete_event", self.close_dialog)
119
self.dialog.connect("response", self.on_response)
121
self.dialog.show_all()
123
def loadDialogSelectUsername(self):
124
path = os.path.dirname(os.path.abspath(__file__))
126
self.dialog_select_username = self.builder.get_object("dlg_select_username")
127
if not self.dialog_select_username or len(self.usernames) < 1:
129
self.dialog_select_username.set_title("Select username")
130
self.dialog_select_username.set_transient_for(self.dialog)
131
self.dialog_select_username.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
132
# TODO: Handle ok and cancel buttons
133
self.dialog_select_username.connect("response", self.on_response_select_username)
134
self.dialog_select_username.connect("delete_event", self.close_dialog_select_username)
136
username_model = gtk.ListStore(str)
137
self.select_username = self.builder.get_object("select_username")
138
self.select_username.set_model(username_model)
139
for u in self.usernames:
140
self.select_username.append_text(u)
141
self.select_username.set_active(0)
143
self.dialog_select_username.show_all()
145
def print_selected(self, widget, data=None):
146
print self.select_username.get_active()
148
def close_dialog(self, widget, data=None):
149
self.dialog.destroy()
152
def close_dialog_select_username(self, widget, data=None):
153
self.dialog_select_username.destroy()
157
def on_import_json_activate(self, widget):
160
def on_response(self, widget, response_id):
161
if response_id == -7 or response_id == -4:
162
self.close_dialog(widget)
163
elif response_id == 0 and self.txtImport:
164
self.import_json(widget)
166
print "Error: Unknown response id %d" %(response_id)
168
def on_response_select_username(self, widget, response_id):
169
if response_id == -7:
170
self.dialog.show_all()
171
self.close_dialog_select_username(widget)
172
elif response_id == -4:
173
self.close_dialog_select_username(widget)
174
elif response_id == 0:
175
self.import_tasks(widget)
176
self.close_dialog_select_username(widget)
178
print "Error: Unknown response id %d" %(response_id)
181
def import_json(self, widget):
182
url = self.txtImport.get_text()
183
json_text = readurl(url)
185
# TODO: Pop up error dialog
186
print "Error: Could not load url %s" % url
190
self.json_tasks = json.loads(json_text)
192
# TODO: Create listing of usernames available
194
for specname,spec in self.json_tasks['specs'].items():
195
for wi in spec['work_items']:
196
if wi['status'] != "todo":
198
if wi['assignee'] in self.usernames:
200
if not wi['assignee']:
202
self.usernames.append(wi['assignee'])
203
self.usernames.sort()
205
# Pop up dialog allowing user to select username
206
self.loadDialogSelectUsername()
207
self.dialog.hide_all()
208
self.dialog_select_username.run()
210
def import_tasks(self, widget):
211
username = self.usernames[self.select_username.get_active()]
212
re_dehtml = re.compile(r'<.*?>')
214
for specname,spec in self.json_tasks['specs'].items():
215
for wi in spec['work_items']:
216
if wi['assignee'] != username:
218
if wi['status'] != 'todo':
222
if spec['details_url']:
223
text = spec['details_url']
226
task = self.plugin_api.get_requester().new_task(pid=None, tags=None, newtask=True)
227
task.set_title(re_dehtml.sub('', wi['description']))
228
task.set_text(re_dehtml.sub('', text))
230
# TODO: Do something with spec['priority']
232
self.close_dialog_select_username(widget)
233
self.close_dialog(widget)