[python-bugzilla] [PATCH] Bug cloning

Martin Cermak mcermak at redhat.com
Wed Jan 30 07:24:37 UTC 2013


Hi, I belive that cloning a bug is a useful feature. Below I'm
attaching a patch that should do the trick. I attempted to follow
the behaviour of Bugzilla's web UI:

----------------8<---------------------------------------------

diff --git a/bugzilla/base.py b/bugzilla/base.py
index 7748e2b..7e148c4 100644
--- a/bugzilla/base.py
+++ b/bugzilla/base.py
@@ -618,6 +618,17 @@ class BugzillaBase(object):
         You may also add a "nomail":1 item, which will suppress email if set.'''
         raise NotImplementedError
 
+    #---- Methods for cloning bugs.
+
+    def _clonebug(self,id,product=None,component=None,version=None):
+        '''IMPLEMENT ME: Clone the given bug. This is the raw call, and no data checking is
+        done here. That's up to the clonebug method.'''
+        raise NotImplementedError
+
+    def clonebug(self,product=None,component=None,version=None):
+        '''Clone the given bug.'''
+        return self.bugzilla._clonebug(self.bug_id,product,component,version)
+
     #---- Methods for working with attachments
 
     def _attachment_encode(self,fh):
@@ -1305,6 +1316,11 @@ class _Bug(object):
         '''An alias for refresh()'''
         self.refresh()
 
+    def clonebug(self,product=None,component=None,version=None):
+        '''Clone the given bug. This is the raw call, and no data checking is
+        done here. That's up to the clonebug method.'''
+        return self.bugzilla._clonebug(self.bug_id,product,component,version)
+
     def setstatus(self,status,comment='',private=False,private_in_it=False,nomail=False):
         '''Update the status for this bug report.
         Valid values for status are listed in querydefaults['bug_status_list']
diff --git a/bugzilla/rhbugzilla.py b/bugzilla/rhbugzilla.py
index 4233d32..59fb418 100644
--- a/bugzilla/rhbugzilla.py
+++ b/bugzilla/rhbugzilla.py
@@ -130,6 +130,17 @@ class RHBugzilla(Bugzilla4):
         requestee (as in: needinfo from smartguy at answers.com). Alas.'''
         return self._proxy.bugzilla.updateFlags(id,flags)
 
+    #---- Methods for cloning bugs.
+
+    def _clonebug(self,id,product=None,component=None,version=None):
+        '''IMPLEMENT MEClone the given bug. This is the raw call, and no data checking is
+        done here. That's up to the clonebug method.'''
+        raise NotImplementedError
+
+    def clonebug(self,product=None,component=None,version=None):
+        '''Clone the given bug.'''
+        return self.bugzilla._clonebug(self.bug_id,product,component,version)
+
     #---- Methods for working with attachments
 
     # If your bugzilla wants attachments in something other than base64, you
@@ -281,6 +292,94 @@ class RHBugzilla(Bugzilla4):
         return self._close_bug(id, update)
         #return self._update_bug(id,update)
 
+    def _clonebug(self,id,product=None,component=None,version=None):
+        '''Clone this bug similarly as one can do it in the webui'''
+        origbug = self.getbug(id)
+        clone_message = '+++ This bug was initially created as a clone of Bug #%s +++\n\n' % (origbug.id)
+        isprivate = False
+        isadditional = False
+        for rec in origbug.longdescs:
+                if isadditional:
+                        clone_message += '\n--- Additional comment from '
+                        clone_message += rec['author']['realname']
+                        clone_message += ' on '
+                        clone_message += rec['time']
+                        clone_message += ' EDT ---\n\n'
+                if 'extra_data' in rec.keys():
+                        clone_message += '\n\n*** This bug has been marked as a duplicate of bug %s ***\n' % (rec['extra_data'])
+                else:
+                        clone_message += rec['body'] + "\n"
+                isadditional = True
+                if rec['isprivate'] == 1:
+                        isprivate = True
+        cc = origbug.cc
+        cc.append(origbug.reporter)
+        depends_on = str(origbug.bug_id) + ' '
+        depends_on += ' '.join([str(i) for i in origbug.dependson])
+        new_product = product
+        if new_product == None: new_product = origbug.product
+        new_component = component
+        if new_component == None: new_component = origbug.component
+        new_version = version
+        new_target_release = None
+        if new_version == None and new_product != origbug.product:
+            releases = self._proxy.Product.get({'names': new_product, 'include_fields': ['releases']})
+            prodinfo = releases['products'][0]['releases']
+            versions = [p['name'] for p in prodinfo]
+            new_version = versions[-1]
+        elif new_version == None:
+             new_target_release = origbug.target_release
+             new_version = origbug.version
+        kwargs = {
+            'product': new_product,
+            'component': new_component,
+            'version': new_version,
+            'op_sys': origbug.op_sys,
+            'platform': origbug.platform,
+            'summary': origbug.summary,
+            'description': clone_message,
+            'comment_is_private': isprivate,
+            'priority': origbug.priority,
+            'bug_severity': origbug.bug_severity,
+            'depends_on': depends_on,
+            'blocked': origbug.blocked,
+            'whiteboard': origbug.whiteboard,
+            'keywords': origbug.keywords,
+            'cc': cc,
+            'estimated_time': origbug.estimated_time,
+            'remaining_time': origbug.remaining_time,
+            'deadline': origbug.deadline,
+            'url': origbug.bug_file_loc,
+            'target_release': new_target_release
+            }
+        for key in kwargs.keys():
+            if kwargs[key] == None:
+                del kwargs[key]
+        newbug = self.createbug(**kwargs)
+        origgroups = [grp['name'] for grp in origbug.groups if grp['ison'] == 1]
+        update = dict(
+            cf_clone_of = str(origbug.id),
+            groups = dict(add = origgroups),
+            cf_qa_whiteboard = origbug.qa_whiteboard,
+            cf_cust_facing = origbug.cust_facing,
+            cf_devel_whiteboard = origbug.devel_whiteboard,
+            cf_internal_whiteboard = origbug.internal_whiteboard,
+            cf_build_id = origbug.build_id,
+            cf_partner = origbug.partner,
+            cf_verified = ['Any'],
+            cf_environment = origbug.cf_environment
+            )
+        changes = self._update_bug(newbug.id,update)
+        # external tracker references
+        extbugs = []
+        for eb in origbug.external_bugs:
+                neb = {}
+                neb['ext_bz_bug_id'] = eb['ext_bz_bug_id']
+                neb['ext_type_id'] = eb['type']['id']
+                extbugs.append(neb)
+        self._proxy.ExternalBugs.add_external_bug({'bug_ids': [newbug.id], 'external_bugs': extbugs})
+        return newbug
+
     def _setassignee(self,id,**data):
         '''Raw xmlrpc call to set one of the assignee fields on a bug.
         changeAssignment($id, $data, $username, $password)

----------------8<---------------------------------------------

Martin


More information about the python-bugzilla mailing list