# HG changeset patch # User Goffi # Date 1507280154 -7200 # Node ID d2476dc2d55d13d3729082172461b6c0bd6b051e # Parent 95a41c5f67c0406f2e42d8c7e08e3c75797f2c16 plugin tickets import Bugzilla: Bugzilla tickets importer: it imports tickets from Bugzilla's XML export format. diff -r 95a41c5f67c0 -r d2476dc2d55d src/plugins/plugin_tickets_import_bugzilla.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/plugin_tickets_import_bugzilla.py Fri Oct 06 10:55:54 2017 +0200 @@ -0,0 +1,117 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# SàT plugin for import external blogs +# Copyright (C) 2009-2016 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 . + +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 + + +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 +""") + + +class BugzillaParser(object): + + + 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['creation'] = bug.findtext('creation_ts') + ticket['update'] = bug.findtext('delta_ts') + ticket['title'] = bug.findtext('short_desc') + reporter_elt = bug.find('reporter') + ticket['reporter_name'] = reporter_elt.get('name') + ticket['reporter_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') + ticket['severity'] = bug.findtext('bug_severity') + 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'] = bug.findtext('bug_status') + 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 = {'from': who.text, + 'date': longdesc.findtext('bug_when'), + 'nick': who.get('name'), + 'body': 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