Mercurial > libervia-backend
comparison sat/plugins/plugin_tickets_import_bugzilla.py @ 2562:26edcf3a30eb
core, setup: huge cleaning:
- moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention
- move twisted directory to root
- removed all hacks from setup.py, and added missing dependencies, it is now clean
- use https URL for website in setup.py
- removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed
- renamed sat.sh to sat and fixed its installation
- added python_requires to specify Python version needed
- replaced glib2reactor which use deprecated code by gtk3reactor
sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 02 Apr 2018 19:44:50 +0200 |
parents | src/plugins/plugin_tickets_import_bugzilla.py@0046283a285d |
children | 3e4e78de9cca |
comparison
equal
deleted
inserted
replaced
2561:bd30dc3ffe5a | 2562:26edcf3a30eb |
---|---|
1 #!/usr/bin/env python2 | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SàT plugin for import external blogs | |
5 # Copyright (C) 2009-2018 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 from sat.tools import utils | |
30 | |
31 | |
32 PLUGIN_INFO = { | |
33 C.PI_NAME: "Bugzilla import", | |
34 C.PI_IMPORT_NAME: "IMPORT_BUGZILLA", | |
35 C.PI_TYPE: C.PLUG_TYPE_BLOG, | |
36 C.PI_DEPENDENCIES: ["TICKETS_IMPORT"], | |
37 C.PI_MAIN: "BugzillaImport", | |
38 C.PI_HANDLER: "no", | |
39 C.PI_DESCRIPTION: _("""Tickets importer for Bugzilla""") | |
40 } | |
41 | |
42 SHORT_DESC = D_(u"import tickets from Bugzilla xml export file") | |
43 | |
44 LONG_DESC = D_(u"""This importer handle Bugzilla xml export file. | |
45 | |
46 To use it, you'll need to export tickets using XML. | |
47 Tickets will be uploaded with the same ID as for Bugzilla, any existing ticket with this ID will be replaced. | |
48 | |
49 location: you must use the absolute path to your .xml file | |
50 """) | |
51 | |
52 STATUS_MAP = { | |
53 'NEW': 'queued', | |
54 'ASSIGNED': 'started', | |
55 'RESOLVED': 'review', | |
56 'CLOSED': 'closed', | |
57 'REOPENED': 'started' # we loose data here because there is no need on basic workflow to have a reopened status | |
58 } | |
59 | |
60 | |
61 class BugzillaParser(object): | |
62 # TODO: add a way to reassign values | |
63 | |
64 def parse(self, file_path): | |
65 tickets = [] | |
66 root = etree.parse(file_path) | |
67 | |
68 for bug in root.xpath('bug'): | |
69 ticket = {} | |
70 ticket['id'] = bug.findtext('bug_id') | |
71 ticket['created'] = utils.date_parse(bug.findtext('creation_ts')) | |
72 ticket['updated'] = utils.date_parse(bug.findtext('delta_ts')) | |
73 ticket['title'] = bug.findtext('short_desc') | |
74 reporter_elt = bug.find('reporter') | |
75 ticket['author'] = reporter_elt.get('name') | |
76 if ticket['author'] is None: | |
77 if '@' in reporter_elt.text: | |
78 ticket['author'] = reporter_elt.text[:reporter_elt.text.find('@')].title() | |
79 else: | |
80 ticket['author'] = u'no name' | |
81 ticket['author_email'] = reporter_elt.text | |
82 assigned_to_elt = bug.find('assigned_to') | |
83 ticket['assigned_to_name'] = assigned_to_elt.get('name') | |
84 ticket['assigned_to_email'] = assigned_to_elt.text | |
85 ticket['cc_emails'] = [e.text for e in bug.findall('cc')] | |
86 ticket['priority'] = bug.findtext('priority').lower().strip() | |
87 ticket['severity'] = bug.findtext('bug_severity').lower().strip() | |
88 ticket['product'] = bug.findtext('product') | |
89 ticket['component'] = bug.findtext('component') | |
90 ticket['version'] = bug.findtext('version') | |
91 ticket['platform'] = bug.findtext('rep_platform') | |
92 ticket['os'] = bug.findtext('op_sys') | |
93 ticket['status'] = STATUS_MAP.get(bug.findtext('bug_status'), 'queued') | |
94 ticket['milestone'] = bug.findtext('target_milestone') | |
95 | |
96 | |
97 body = None | |
98 comments = [] | |
99 for longdesc in bug.findall('long_desc'): | |
100 if body is None: | |
101 body = longdesc.findtext('thetext') | |
102 else: | |
103 who = longdesc.find('who') | |
104 comment = {'id': longdesc.findtext('commentid'), | |
105 'author_email': who.text, | |
106 'published': utils.date_parse(longdesc.findtext('bug_when')), | |
107 'author': who.get('name', who.text), | |
108 'content': longdesc.findtext('thetext')} | |
109 comments.append(comment) | |
110 | |
111 ticket['body'] = body | |
112 ticket['comments'] = comments | |
113 tickets.append(ticket) | |
114 | |
115 tickets.sort(key = lambda t: int(t['id'])) | |
116 return (tickets, len(tickets)) | |
117 | |
118 | |
119 class BugzillaImport(object): | |
120 | |
121 def __init__(self, host): | |
122 log.info(_(u"Bugilla Import plugin initialization")) | |
123 self.host = host | |
124 host.plugins['TICKETS_IMPORT'].register('bugzilla', self.Import, SHORT_DESC, LONG_DESC) | |
125 | |
126 def Import(self, client, location, options=None): | |
127 if not os.path.isabs(location): | |
128 raise exceptions.DataError(u"An absolute path to XML data need to be given as location") | |
129 bugzilla_parser = BugzillaParser() | |
130 # d = threads.deferToThread(bugzilla_parser.parse, location) | |
131 d = defer.maybeDeferred(bugzilla_parser.parse, location) | |
132 return d |