comparison src/plugins/plugin_tickets_import_bugzilla.py @ 2373:d2476dc2d55d

plugin tickets import Bugzilla: Bugzilla tickets importer: it imports tickets from Bugzilla's XML export format.
author Goffi <goffi@goffi.org>
date Fri, 06 Oct 2017 10:55:54 +0200
parents
children a49a19f06e38
comparison
equal deleted inserted replaced
2372:95a41c5f67c0 2373:d2476dc2d55d
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3
4 # SàT plugin for import external blogs
5 # Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 from sat.core.i18n import _, D_
21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger
23 log = getLogger(__name__)
24 from sat.core import exceptions
25 # from twisted.internet import threads
26 from twisted.internet import defer
27 import os.path
28 from lxml import etree
29
30
31 PLUGIN_INFO = {
32 C.PI_NAME: "Bugzilla import",
33 C.PI_IMPORT_NAME: "IMPORT_BUGZILLA",
34 C.PI_TYPE: C.PLUG_TYPE_BLOG,
35 C.PI_DEPENDENCIES: ["TICKETS_IMPORT"],
36 C.PI_MAIN: "BugzillaImport",
37 C.PI_HANDLER: "no",
38 C.PI_DESCRIPTION: _("""Tickets importer for Bugzilla""")
39 }
40
41 SHORT_DESC = D_(u"import tickets from Bugzilla xml export file")
42
43 LONG_DESC = D_(u"""This importer handle Bugzilla xml export file.
44
45 To use it, you'll need to export tickets using XML.
46 Tickets will be uploaded with the same ID as for Bugzilla, any existing ticket with this ID will be replaced.
47
48 location: you must use the absolute path to your .xml file
49 """)
50
51
52 class BugzillaParser(object):
53
54
55 def parse(self, file_path):
56 tickets = []
57 root = etree.parse(file_path)
58
59 for bug in root.xpath('bug'):
60 ticket = {}
61 ticket['id'] = bug.findtext('bug_id')
62 ticket['creation'] = bug.findtext('creation_ts')
63 ticket['update'] = bug.findtext('delta_ts')
64 ticket['title'] = bug.findtext('short_desc')
65 reporter_elt = bug.find('reporter')
66 ticket['reporter_name'] = reporter_elt.get('name')
67 ticket['reporter_email'] = reporter_elt.text
68 assigned_to_elt = bug.find('assigned_to')
69 ticket['assigned_to_name'] = assigned_to_elt.get('name')
70 ticket['assigned_to_email'] = assigned_to_elt.text
71 ticket['cc_emails'] = [e.text for e in bug.findall('cc')]
72 ticket['priority'] = bug.findtext('priority')
73 ticket['severity'] = bug.findtext('bug_severity')
74 ticket['product'] = bug.findtext('product')
75 ticket['component'] = bug.findtext('component')
76 ticket['version'] = bug.findtext('version')
77 ticket['platform'] = bug.findtext('rep_platform')
78 ticket['os'] = bug.findtext('op_sys')
79 ticket['status'] = bug.findtext('bug_status')
80 ticket['milestone'] = bug.findtext('target_milestone')
81
82
83 body = None
84 comments = []
85 for longdesc in bug.findall('long_desc'):
86 if body is None:
87 body = longdesc.findtext('thetext')
88 else:
89 who = longdesc.find('who')
90 comment = {'from': who.text,
91 'date': longdesc.findtext('bug_when'),
92 'nick': who.get('name'),
93 'body': longdesc.findtext('thetext')}
94 comments.append(comment)
95
96 ticket['body'] = body
97 ticket['comments'] = comments
98 tickets.append(ticket)
99
100 tickets.sort(key = lambda t: int(t['id']))
101 return (tickets, len(tickets))
102
103
104 class BugzillaImport(object):
105
106 def __init__(self, host):
107 log.info(_(u"Bugilla Import plugin initialization"))
108 self.host = host
109 host.plugins['TICKETS_IMPORT'].register('bugzilla', self.Import, SHORT_DESC, LONG_DESC)
110
111 def Import(self, client, location, options=None):
112 if not os.path.isabs(location):
113 raise exceptions.DataError(u"An absolute path to XML data need to be given as location")
114 bugzilla_parser = BugzillaParser()
115 # d = threads.deferToThread(bugzilla_parser.parse, location)
116 d = defer.maybeDeferred(bugzilla_parser.parse, location)
117 return d