def test_012_get_back_linked_work_items(self): req = Requirement(project_id=DEFAULT_PROJ, work_item_id=self.work_item_id_2) items = req.get_back_linked_work_items() self.assertTrue(len(items) == 1) wi = items[0] self.assertEqual(wi.work_item_id, self.work_item_id)
def requirement(source_code_path, project): """Create and/or update requirements in Polarion.""" requirements = [] source_testcases = itertools.chain( *collector.collect_tests(source_code_path).values()) for testcase in source_testcases: fields = {k.lower(): v for k, v in testcase.fields.items()} if ('requirement' in fields and fields['requirement'] not in requirements): requirement_title = fields['requirement'] results = Requirement.query(requirement_title, fields=['status', 'title']) requirement = None for result in results: if result.title == requirement_title: requirement = result break if requirement is None: click.echo(u'Creating requirement {0}.'.format( fields['requirement'])) requirement = Requirement.create(project, requirement_title, '', reqtype='functional') if requirement.status != 'approved': click.echo(u'Approving requirement {0}.'.format( requirement.title)) requirement.status = 'approved' requirement.update()
def fetch_requirement(query, project, collect_only=False): """Fetch or create a requirement. Return the fetched or created requirement object. """ click.echo('Fetching requirement {0}.'.format(query)) if query in OBJ_CACHE['requirements'].keys(): return OBJ_CACHE['requirements'][query] requirement = None if not collect_only: results = Requirement.query(query, fields=['title', 'work_item_id']) if len(results) > 0: # As currently is not possible to get a single # match for the title, make sure to not use a # not intended Requirement. for result in results: if result.title == query or result.work_item_id == query: requirement = result if requirement is None: click.echo('Creating requirement {0}.'.format(query)) if not collect_only: requirement = Requirement.create(project, query, '', reqtype='functional') requirement.status = 'approved' requirement.update() if query not in OBJ_CACHE['requirements'].keys(): OBJ_CACHE['requirements'][query] = requirement return requirement
def test_013_multiple_types(self): req = Requirement() tc = TestCase() with self.assertRaises(AttributeError): tc.reqtype with self.assertRaises(AttributeError): req.caseimportance req.title = "req1" tc.title = "tc1" self.assertNotEqual(req.title, tc.title)
def test_custom_workitem(self): tc2 = TestCase(project_id=DEFAULT_PROJ, work_item_id=self.TEST_CASE_ID) self.assertIsNotNone(tc2.caseautomation) with self.assertRaises(PylarionLibException): tc2.caseautomation = "bad" tc2.caseautomation = "automated" # check for shared memory issue self.assertNotEqual(self.tc.caseautomation, tc2.caseautomation) req = Requirement() with self.assertRaises(PylarionLibException): req.reqtype = "bad" req.reqtype = 'functional'
def setUpClass(cls): req1 = Requirement.create(DEFAULT_PROJ, "regression", "regression", severity="should_have", reqtype="functional") cls.NEW_REQ = req1.work_item_id req2 = Requirement.create(DEFAULT_PROJ, "regression", "regression", severity="should_have", reqtype="functional") cls.NEW_REQ2 = req2.work_item_id
def list_workitems_by_query(self, query, wi_type): fields = ['work_item_id', 'title', 'author', 'created'] if wi_type in ["testcase", "TestCase"]: workitem_list = TestCase.query(query, fields) elif wi_type in ["requirement", "Requirement"]: workitem_list = Requirement.query(query, fields) elif wi_type == '': workitem_list = TestCase.query(query, fields) + \ Requirement.query(query, fields) else: print("'%s' is invalid. Use testcase or requirement" % wi_type) exit(0) return workitem_list
def print_links_for_requirement(self, req_id): req = Requirement(Requirement.default_project, req_id) print('ID%-12sRole' % ('')) print('-------%7s------' % ('')) for linked in req.linked_work_items_derived: print('%-13s %-13s' % (linked.work_item_id, linked.role))
def clean_req_title(req): """ Makes sure that all the titles for requirements have RHSM-REQ : title (space:space) :param req: A Pylarion Requirement object :return: """ patt = re.compile(r"RHSM-REQ(\s*:?\s*)(.+)") m = patt.search(req.title) if not m: raise Exception("Did not match on requirement title") colon, title = m.groups() if not colon.startswith(" ") or not colon.endswith(" "): new_title = "RHSM-REQ : " + title preq = Requirement(uri=req.uri) preq.title = new_title try: preq.update() print "Changed {} to title {}".format(preq.work_item_id, preq.title) except: print "Failed to update {}".format(req.work_item_id)
def create_requirements(bz_rfes, bz_connection): for bz_rfe in bz_rfes: bug_title, bug_product, named_parms, bug_description, bug_link, bug_id, bug_priority, bug_severity, bug_dfg = get_bug_params(bz_rfe) bug_title = "BZ_id=%s; %s" % (bug_id, bug_title) print "\n%s - start bug %s" % (datetime.datetime.now(), bug_id), print '"{}"'.format(bug_link) # Convert bugzilla to Polarion product and set product = PROJ_DICT[bug_product] if isRequirementInPolarion(bug_id, product) == False: #convert args, for now leave out Priority #named_parms["priority"] = PRIOR_DICT[bug_priority] named_parms["severity"] = SEV_DICT[bug_severity] # Set Polarion requirement type named_parms["reqtype"] = "functional" #Convert DFG name from bugzilla to dfg_id in Polarion named_parms["d_f_g"] = convert_polarion_dfg(bug_dfg) #Get bug description from first comment and add to Polarion requirement desc = "" if bug_description: desc = Text(bug_description.encode('ascii', 'ignore').decode('ascii')) # decode("utf-8")) desc.content_type = "text/plain" # Add hyperlink to bugzilla link = Hyperlink() link.role = "ref_ext" link.uri = bug_link for i in range(0,10): #WA for Polarion disconnection from time to time try: req = Requirement.create(project_id=product, title=bug_title, desc=desc, **named_parms) break except Exception as inst: print inst i+=1 time.sleep(10) req.add_hyperlink(link.uri, link.role) req.status = "approved" req.customerscenario = True req.update() #[Anjali] one last step here is 'set 'qe-test-coverage' flag in Bugzilla (to show that the bugzilla has a test coverage) bz_connection.add_external_tracker(str(bz_rfe.id), str(req.work_item_id), ext_type_description="Polarion Requirement") custflag = {'qe_test_coverage': '-'} #doesn't matter if we send bugs as a list or not as the method fixes it anyway #this currently does not work it returns an error, so I am disabling it #Fault -32000: 'Not an ARRAY reference at /var/www/html/bugzilla/Bugzilla/WebService/Util.pm line 44.\n #bz_connection.update_flags([bug_id], custflag) print "%s - end bug: %s - %s" % (datetime.datetime.now(), req.work_item_id, link.uri)
def isRequirementInPolarion(bug_link): for i in range(0, 10): # WA for Polarion disconnection from time to time try: if Requirement.query('"{}"'.format(bug_link)): print "\nRequirement already in Polarion: " + str(bug_link) return True break except Exception as inst: print inst i += 1 time.sleep(10) return False
def query_requirement(query, fields=None, **kwargs): """ Returns a list of pylarion Requirement objects :param query: :param fields: :param kwargs: :return: """ if fields is None: fields = ["work_item_id", "title"] from pylarion.work_item import Requirement return Requirement.query(query, fields=fields, **kwargs)
def test_007_add_linked_work_item(self): tc = TestCase(project_id=DEFAULT_PROJ, work_item_id=self.work_item_id) self.assertTrue(tc.add_linked_item(self.work_item_id_2, "verifies")) tc2 = TestCase(project_id=DEFAULT_PROJ, work_item_id=self.work_item_id) self.assertTrue(len(tc2.linked_work_items) == 1) link = tc2.linked_work_items[0] self.assertEqual(link.work_item_id, self.work_item_id_2) self.assertEqual(link.role, "verifies") req3 = Requirement(project_id=DEFAULT_PROJ, work_item_id=self.work_item_id_2) self.assertTrue(len(req3.linked_work_items_derived) == 1) link = req3.linked_work_items_derived[0] self.assertEqual(link.work_item_id, self.work_item_id) self.assertEqual(link.role, "verifies")
def fetch_requirement(query, project, collect_only=False): """Fetch or create a requirement. Return the fetched or created requirement object. """ click.echo( 'Fetching requirement {0}.'.format(query)) if query in OBJ_CACHE['requirements'].keys(): return OBJ_CACHE['requirements'][query] requirement = None if not collect_only: results = Requirement.query( query, fields=['title', 'work_item_id'] ) if len(results) > 0: # As currently is not possible to get a single # match for the title, make sure to not use a # not intended Requirement. for result in results: if result.title == query or result.work_item_id == query: requirement = result if requirement is None: click.echo( 'Creating requirement {0}.'.format(query)) if not collect_only: requirement = Requirement.create( project, query, '', reqtype='functional' ) requirement.status = 'approved' requirement.update() if query not in OBJ_CACHE['requirements'].keys(): OBJ_CACHE['requirements'][query] = requirement return requirement
def create_requirement(project_id, title, description="", reqtype="functional", severity="should_have"): if True: log.warning("No longer creating a requirement automatically") return None else: from pylarion.work_item import Requirement req = is_requirement_exists(title) if req: log.info("Found existing Requirement {}".format(req.title)) return req else: log.info("Creating a new Requirement: {}".format(title)) return Requirement.create(project_id, title, description, severity=severity, reqtype=reqtype)
def setUpClass(cls): tc = TestCase.create(DEFAULT_PROJ, "regression", "regression", caseimportance="high", caselevel="component", caseautomation="notautomated", caseposneg="positive", testtype="functional", subtype1="-") req = Requirement.create(DEFAULT_PROJ, "regression _link", "regression link", reqtype="functional", severity="should_have") cls.work_item_id = tc.work_item_id cls.work_item_uri = tc.uri cls.work_item_id_2 = req.work_item_id cls.work_item_uri_2 = req.uri
def isRequirementInPolarion(bug_id, bug_project): #i was wrong you can search on hyperlink like this #select tc.c_id from WORKITEM tc inner join PROJECT on PROJECT.C_URI = tc.FK_URI_PROJECT left join struct_workitem_hyperlinks hlink on tc.c_uri=hlink.fk_uri_p_workitem where PROJECT.C_ID = 'Polarion' and tc.C_TYPE = 'requirement' and hlink.c_url LIKE '%1011755%' query_str = "SELECT tc.c_uri from WORKITEM tc join project proj on proj.c_uri=tc.fk_uri_project where proj.c_id='%s' and tc.c_title like ('BZ_id=%s%%') and tc.c_type = 'requirement'" % (bug_project, bug_id) for i in range(0, 10): # WA for Polarion disconnection from time to time try: items = _WorkItem.query(query_str, is_sql=True, fields=["work_item_id", "title"]) if len(items) > 0: for item in items: tc = Requirement(uri=item.uri) print "\n" + tc.work_item_id print "\nRequirement already in Polarion: " + str(bug_id) return True break except Exception as inst: print inst i += 1 time.sleep(10) return False
def add_test_case(args): """Task that creates or updates Test Cases and manages their Requirement. This task relies on ``OBJ_CACHE`` to get the collect_only and project objects. :param args: A tuple where the first element is a path and the second is a list of ``TestFunction`` objects mapping the tests from that path. """ path, tests = args collect_only = OBJ_CACHE['collect_only'] project = OBJ_CACHE['project'] # Fetch or create a Requirement requirement = None requirement_name = parse_requirement_name(path) click.echo( 'Fetching requirement {0}.'.format(requirement_name)) if not collect_only: results = Requirement.query( '{0}'.format(requirement_name), fields=['title', 'work_item_id'] ) if len(results) > 0: # As currently is not possible to get a single # match for the title, make sure to not use a # not intended Requirement. for result in results: if result.title == requirement_name: requirement = result if requirement is None: click.echo( 'Creating requirement {0}.'.format(requirement_name)) if not collect_only: requirement = Requirement.create( project, requirement_name, '', reqtype='functional' ) for test in tests: # Generate the test_case_id. It could be either path.test_name or # path.ClassName.test_name if the test methods is defined within a # class. test_case_id_parts = [ path.replace('/', '.').replace('.py', ''), test.name ] if test.parent_class is not None: test_case_id_parts.insert(-1, test.parent_class) test_case_id = '.'.join(test_case_id_parts) if test.docstring: if not type(test.docstring) == unicode: test.docstring = test.docstring.decode('utf8') test.docstring = RST_PARSER.parse(test.docstring) # Is the test automated? Acceptable values are: # automated, manualonly, and notautomated auto_status = 'automated' if test.automated else 'notautomated' caseposneg = 'negative' if 'negative' in test.name else 'positive' setup = test.setup if test.setup else None results = [] if not collect_only: results = TestCase.query( test_case_id, fields=[ 'caseautomation', 'caseposneg', 'description', 'work_item_id' ] ) if len(results) == 0: click.echo( 'Creating test case {0} for requirement {1}.' .format(test.name, requirement_name) ) if not collect_only: test_case = TestCase.create( project, test.name, test.docstring if test.docstring else '', caseautomation=auto_status, casecomponent='-', caseimportance='medium', caselevel='component', caseposneg=caseposneg, subtype1='-', test_case_id=test_case_id, testtype='functional', setup=setup, ) click.echo( 'Linking test case {0} to verify requirement {1}.' .format(test.name, requirement_name) ) if not collect_only: test_case.add_linked_item( requirement.work_item_id, 'verifies') else: click.echo( 'Updating test case {0} for requirement {1}.' .format(test.name, requirement_name) ) # Ensure that a single match for the Test Case is # returned. assert len(results) == 1 test_case = results[0] if (not collect_only and (test_case.description != test.docstring or test_case.caseautomation != auto_status or test_case.caseposneg != caseposneg or test_case.setup != setup)): test_case.description = ( test.docstring if test.docstring else '') test_case.caseautomation = auto_status test_case.caseposneg = caseposneg test_case.setup = setup test_case.update()
def main(): isUpdateAutomationValue = False # access excel file and update results credentials = get_credentials() http = credentials.authorize(httplib2.Http()) discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?version=v4') service = discovery.build('sheets', 'v4', http=http, discoveryServiceUrl=discoveryUrl) # https://docs.google.com/spreadsheets/d/1y4eBJhcZ0HsB5JUH5MPcFXWtc2zbP9XbgdCIF0S4iHs/edit#gid=1503195790 spreadsheetId = '1y4eBJhcZ0HsB5JUH5MPcFXWtc2zbP9XbgdCIF0S4iHs' # Get all test runs by Polarion query, extract test run id and test run results (pass, fail, pending block, total...) test_runs_uris = TestRun.search( 'NOT status:invalid AND plannedin.KEY:RHOS16' ) #updated:[20190627 TO 20190630]') # # test_runs_uris = TestRun.search('20180625-0836') print("Number of items %s" % len(test_runs_uris)) loop_counter = 1 missing_test_run_in_excel = '' non_test_cases_item = 0 for test_run_uri in test_runs_uris: # for i in range(106,130): # test_run_uri = test_runs_uris[i] #get excel values rangeName = 'RHOS 16!A2:X' result = service.spreadsheets().values().get( spreadsheetId=spreadsheetId, range=rangeName).execute() values = result.get('values', []) value_input_option = 'RAW' print('Updating test run number: ' + str(loop_counter)) loop_counter += 1 print(test_run_uri.uri) test_run = TestRun(uri=test_run_uri.uri) test_run_id = test_run.test_run_id print('Test run title: ' + test_run.title) print('Test run ID: ' + test_run.test_run_id) records = test_run.records pass_counter = 0 fail_counter = 0 pending_counter = 0 automation_counter = 0.0 critical_counter = 0 critical_auto_counter = 0 #automation_percentage = 0 blocked_counter = 0 total_counter = 0 #Collect inforamtion about test runs, how many test pass if test_run.TestRunType == 'Acceptance': for record in records: if record.result == 'passed': pass_counter += 1 elif record.result == 'failed': fail_counter += 1 elif record.result == 'blocked': blocked_counter += 1 else: pending_counter += 1 else: for record in records: # print record.result #check if test is automated test = TestCase.query(record.test_case_id) # print('Test case ID: ' + record.test_case_id) # Check if the object type is a testcase and not a header for example! if test and not Requirement.query(record.test_case_id): #calculate critical automated and rest automated if isUpdateAutomationValue: if test[0].caseautomation.lower() == 'automated': automation_counter += 1 if test[0].caseimportance.lower() == 'critical': critical_auto_counter += 1 #count number of critical cases if test[0].caseimportance.lower() == 'critical': critical_counter += 1 if record.result == 'passed': pass_counter += 1 elif record.result == 'failed': fail_counter += 1 elif record.result == 'blocked': blocked_counter += 1 else: pending_counter += 1 else: non_test_cases_item += 1 total_counter = pass_counter + fail_counter + blocked_counter + pending_counter # if total_counter > 0: # automation_percentage = int(float(automation_counter)/float(total_counter)) #*100 print('Total pass:'******'Total fail:', fail_counter) print('Total blocked:', blocked_counter) print('Total pending:', pending_counter) print('Total automated:', automation_counter) print('Number of critical:', critical_counter) print('Number of critical auto:', critical_auto_counter) #print ('Automation percentage:', automation_percentage) print('Total number of test cases:', total_counter) #column number in excel file and thier representation as hard coded value row_counter = 1 # offset due to headers title_column_number = 2 total_column_number = 8 pass_column_number = 9 fail_column_number = 10 blocked_column_number = 11 test_run_id_column_number = 20 automation_percentage_column_number = 18 critical_test_number = 22 is_test_run_exist_in_excel = None if not values: print('No data found.') else: for row in values: is_test_run_exist_in_excel = False row_counter += 1 # print(row_counter) # if(row_counter==134): # print('stop') # Check that row contains test run id in cell R AND check that test_run_id is match if row.__len__() >= test_run_id_column_number and row[ test_run_id_column_number] == test_run_id: print('Row number is: ' + str(row_counter)) is_test_run_exist_in_excel = True # print('%s, %s, %s, %s, %s, %s, %s :' % (row[title_column_number], row[total_column_number], row[pass_column_number], row[fail_column_number], row[blocked_column_number],row[automation_percentage_column_number], row[critical_test_number])) values = [[ total_counter, total_counter, pass_counter, fail_counter, blocked_counter ]] body = {'values': values} rangeName = 'RHOS 15!H' + str(row_counter) + ':L' + str( row_counter) result = service.spreadsheets().values().update( spreadsheetId=spreadsheetId, range=rangeName, valueInputOption=value_input_option, body=body).execute() # # update automation percentage field # values = [ # [automation_percentage] # ] # body = { # 'values': values # } # rangeName = 'RHOS 13!S' + str(row_counter) # result = service.spreadsheets().values().update(spreadsheetId=spreadsheetId, range=rangeName, valueInputOption='USER_ENTERED',body=body).execute() # update PQI values... if isUpdateAutomationValue and test_run.TestRunType != 'Acceptance': values = [[ automation_counter, critical_counter, critical_auto_counter ]] body = {'values': values} rangeName = 'RHOS 15!V' + str( row_counter) + ':X' + str(row_counter) result = service.spreadsheets().values().update( spreadsheetId=spreadsheetId, range=rangeName, valueInputOption=value_input_option, body=body).execute() # done with update, move to next test run break #Check if test run exist in excel file and was updated if not is_test_run_exist_in_excel: missing_test_run_in_excel += test_run_id + ", " print("Missing Test Runs in Excel: " + missing_test_run_in_excel) print("Number of headers or requirements in test runs: ", non_test_cases_item)
def test_case(path, collect_only, project): """Sync test cases with Polarion.""" testcases = testimony.get_testcases([path]) for path, tests in testcases.items(): requirement = None for test in tests: # Expect test_case_id to be path.test_name or # path.ClassName.test_name. test_case_id_parts = [ path.replace('/', '.').replace('.py', ''), test.name ] if test.parent_class is not None: test_case_id_parts.insert(-1, test.parent_class) test_case_id = '.'.join(test_case_id_parts) if requirement is None: requirement_name = parse_requirement_name(test_case_id) results = Requirement.query('{0}'.format(requirement_name), fields=['title', 'work_item_id']) if len(results) > 0: # As currently is not possible to get a single # match for the title, make sure to not use a # not intended Requirement. for result in results: if result.title == requirement_name: requirement = result if requirement is None: click.echo( 'Creating requirement {0}.'.format(requirement_name)) if not collect_only: requirement = Requirement.create(project, requirement_name, '', reqtype='functional') results = TestCase.query(test_case_id, fields=['description', 'work_item_id']) if len(results) == 0: click.echo( 'Creating test case {0} for requirement {1}.'.format( test.name, requirement_name)) if not collect_only: test_case = TestCase.create( project, test.name, test.docstring if test.docstring else '', caseautomation='automated', casecomponent='-', caseimportance='medium', caselevel='component', caseposneg='positive', subtype1='-', test_case_id=test_case_id, testtype='functional', ) click.echo( 'Liking test case {0} to verify requirement {1}.'.format( test.name, requirement_name)) if not collect_only: test_case.add_linked_item(requirement.work_item_id, 'verifies') else: click.echo( 'Updating test case {0} for requirement {1}.'.format( test.name, requirement_name)) # Ensure that a single match for the Test Case is # returned. assert len(results) == 1 test_case = results[0] if (not collect_only and test_case.description != test.docstring): test_case = TestCase(project, test_case.work_item_id) test_case.description = (test.docstring if test.docstring else '') test_case.update()
def test_case(path, collect_only, project): """Sync test cases with Polarion.""" testcases = testimony.get_testcases([path]) for path, tests in testcases.items(): requirement = None for test in tests: # Expect test_case_id to be path.test_name or # path.ClassName.test_name. test_case_id_parts = [ path.replace('/', '.').replace('.py', ''), test.name ] if test.parent_class is not None: test_case_id_parts.insert(-1, test.parent_class) test_case_id = '.'.join(test_case_id_parts) if requirement is None: requirement_name = parse_requirement_name(test_case_id) results = Requirement.query( '{0}'.format(requirement_name), fields=['title', 'work_item_id'] ) if len(results) > 0: # As currently is not possible to get a single # match for the title, make sure to not use a # not intended Requirement. for result in results: if result.title == requirement_name: requirement = result if requirement is None: click.echo( 'Creating requirement {0}.'.format(requirement_name)) if not collect_only: requirement = Requirement.create( project, requirement_name, '', reqtype='functional' ) results = TestCase.query( test_case_id, fields=['description', 'work_item_id']) if len(results) == 0: click.echo( 'Creating test case {0} for requirement {1}.' .format(test.name, requirement_name) ) if not collect_only: test_case = TestCase.create( project, test.name, test.docstring if test.docstring else '', caseautomation='automated', casecomponent='-', caseimportance='medium', caselevel='component', caseposneg='positive', subtype1='-', test_case_id=test_case_id, testtype='functional', ) click.echo( 'Liking test case {0} to verify requirement {1}.' .format(test.name, requirement_name) ) if not collect_only: test_case.add_linked_item( requirement.work_item_id, 'verifies') else: click.echo( 'Updating test case {0} for requirement {1}.' .format(test.name, requirement_name) ) # Ensure that a single match for the Test Case is # returned. assert len(results) == 1 test_case = results[0] if (not collect_only and test_case.description != test.docstring): test_case = TestCase(project, test_case.work_item_id) test_case.description = ( test.docstring if test.docstring else '') test_case.update()
def create_requirements(bz_rfes, bz_connection): idx = 0 plan = Plan(project_id=POLARION_PRODUCT, plan_id=POLARION_VERSION) req_ids = list() # bz_rfe = bz_rfes[0] #for x in range(103,127): for bz_rfe in bz_rfes: #bz_rfe = bz_rfes[x] bug_title, named_parms, bug_description, bug_link, bug_id, bug_priority, bug_severity, bug_dfg = get_bug_params( bz_rfe) print "\n%s - start bug %s" % (datetime.datetime.now(), idx), idx += 1 print '"{}"'.format(bug_link) if isRequirementInPolarion(bug_link) == False: #TODO Convert bugzilla to Polarion priority and set #named_parms["priority"] = convert_polarion_priority(bug_priority) # Convert bugzilla to Polarion severity and set named_parms["severity"] = convert_polarion_severity(bug_severity) # Set Polarion requirement type named_parms["reqtype"] = "functional" #Cenvert DFG name from bugzilla to dfg_id in Polarion named_parms["d_f_g"] = convert_polarion_dfg(bug_dfg) #Get bug description from first comment and add to Polarion requirement desc = "" if bug_description: desc = Text( bug_description.encode('ascii', 'ignore').decode('ascii')) # decode("utf-8")) desc.content_type = "text/plain" # Add hyperlink to bugzilla link = Hyperlink() link.role = "ref_ext" link.uri = bug_link req = Requirement.create(project_id=POLARION_PRODUCT, title=bug_title, desc=desc, **named_parms) req.add_hyperlink(link.uri, link.role) req.status = "approved" req.update() #Get requirement ID and update bugzilla extrenal link tracker bz_connection.add_external_tracker( str(bz_rfe.id), str(req.work_item_id), ext_type_description="Polarion Requirement") req_ids.append(req.work_item_id) print "%s - end bug: %s" % (datetime.datetime.now(), bug_link) plan.add_plan_items(req_ids)