Mercurial > libervia-backend
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sat/plugins/plugin_tickets_import_bugzilla.py Mon Apr 02 19:44:50 2018 +0200 @@ -0,0 +1,132 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# SàT plugin for import external blogs +# Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from sat.core.i18n import _, D_ +from sat.core.constants import Const as C +from sat.core.log import getLogger +log = getLogger(__name__) +from sat.core import exceptions +# from twisted.internet import threads +from twisted.internet import defer +import os.path +from lxml import etree +from sat.tools import utils + + +PLUGIN_INFO = { + C.PI_NAME: "Bugzilla import", + C.PI_IMPORT_NAME: "IMPORT_BUGZILLA", + C.PI_TYPE: C.PLUG_TYPE_BLOG, + C.PI_DEPENDENCIES: ["TICKETS_IMPORT"], + C.PI_MAIN: "BugzillaImport", + C.PI_HANDLER: "no", + C.PI_DESCRIPTION: _("""Tickets importer for Bugzilla""") +} + +SHORT_DESC = D_(u"import tickets from Bugzilla xml export file") + +LONG_DESC = D_(u"""This importer handle Bugzilla xml export file. + +To use it, you'll need to export tickets using XML. +Tickets will be uploaded with the same ID as for Bugzilla, any existing ticket with this ID will be replaced. + +location: you must use the absolute path to your .xml file +""") + +STATUS_MAP = { + 'NEW': 'queued', + 'ASSIGNED': 'started', + 'RESOLVED': 'review', + 'CLOSED': 'closed', + 'REOPENED': 'started' # we loose data here because there is no need on basic workflow to have a reopened status +} + + +class BugzillaParser(object): + # TODO: add a way to reassign values + + def parse(self, file_path): + tickets = [] + root = etree.parse(file_path) + + for bug in root.xpath('bug'): + ticket = {} + ticket['id'] = bug.findtext('bug_id') + ticket['created'] = utils.date_parse(bug.findtext('creation_ts')) + ticket['updated'] = utils.date_parse(bug.findtext('delta_ts')) + ticket['title'] = bug.findtext('short_desc') + reporter_elt = bug.find('reporter') + ticket['author'] = reporter_elt.get('name') + if ticket['author'] is None: + if '@' in reporter_elt.text: + ticket['author'] = reporter_elt.text[:reporter_elt.text.find('@')].title() + else: + ticket['author'] = u'no name' + ticket['author_email'] = reporter_elt.text + assigned_to_elt = bug.find('assigned_to') + ticket['assigned_to_name'] = assigned_to_elt.get('name') + ticket['assigned_to_email'] = assigned_to_elt.text + ticket['cc_emails'] = [e.text for e in bug.findall('cc')] + ticket['priority'] = bug.findtext('priority').lower().strip() + ticket['severity'] = bug.findtext('bug_severity').lower().strip() + ticket['product'] = bug.findtext('product') + ticket['component'] = bug.findtext('component') + ticket['version'] = bug.findtext('version') + ticket['platform'] = bug.findtext('rep_platform') + ticket['os'] = bug.findtext('op_sys') + ticket['status'] = STATUS_MAP.get(bug.findtext('bug_status'), 'queued') + ticket['milestone'] = bug.findtext('target_milestone') + + + body = None + comments = [] + for longdesc in bug.findall('long_desc'): + if body is None: + body = longdesc.findtext('thetext') + else: + who = longdesc.find('who') + comment = {'id': longdesc.findtext('commentid'), + 'author_email': who.text, + 'published': utils.date_parse(longdesc.findtext('bug_when')), + 'author': who.get('name', who.text), + 'content': longdesc.findtext('thetext')} + comments.append(comment) + + ticket['body'] = body + ticket['comments'] = comments + tickets.append(ticket) + + tickets.sort(key = lambda t: int(t['id'])) + return (tickets, len(tickets)) + + +class BugzillaImport(object): + + def __init__(self, host): + log.info(_(u"Bugilla Import plugin initialization")) + self.host = host + host.plugins['TICKETS_IMPORT'].register('bugzilla', self.Import, SHORT_DESC, LONG_DESC) + + def Import(self, client, location, options=None): + if not os.path.isabs(location): + raise exceptions.DataError(u"An absolute path to XML data need to be given as location") + bugzilla_parser = BugzillaParser() + # d = threads.deferToThread(bugzilla_parser.parse, location) + d = defer.maybeDeferred(bugzilla_parser.parse, location) + return d