Minor enhancements
This commit is contained in:
Vendored
+3
-1
@@ -9,7 +9,9 @@
|
|||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${file}",
|
"program": "${file}",
|
||||||
"console": "integratedTerminal"
|
"console": "integratedTerminal",
|
||||||
|
"justMyCode": false,
|
||||||
|
"pythonArgs": ["-Xfrozen_modules=off"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from jira import JIRA, Issue, exceptions
|
||||||
|
import logging
|
||||||
|
|
||||||
|
def get_assignee(type, issue):
|
||||||
|
isBuergerfrontend = False
|
||||||
|
if type == 'Dev Backend':
|
||||||
|
return 'reu9001'
|
||||||
|
elif type == 'Dev Frontend':
|
||||||
|
for component in issue.fields.components:
|
||||||
|
isBuergerfrontend = component.name == "Bürgerfrontend"
|
||||||
|
if isBuergerfrontend:
|
||||||
|
return 'reu9001'
|
||||||
|
return 'thh9001'
|
||||||
|
elif type.startswith('Test-Anpassung'):
|
||||||
|
return 'ada9001'
|
||||||
|
|
||||||
|
def create_subtask(issue, parent, type, subtask_sp, estimation_remark):
|
||||||
|
escaped = issue.fields.summary.replace('"', r'\"')
|
||||||
|
logging.info(f'summary ~ \"{escaped}\"')
|
||||||
|
|
||||||
|
try:
|
||||||
|
subtask = jira_server.search_issues(f'summary ~ \"{type} {escaped}\"')
|
||||||
|
if subtask:
|
||||||
|
raise exceptions.JIRAError
|
||||||
|
except exceptions.JIRAError:
|
||||||
|
logging.info(f'Skip creating Sub-Task {type} for {issue} as it already exists')
|
||||||
|
return
|
||||||
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# find comments with estimations out of pre-planning
|
||||||
|
# zisco 2021-09-30 19:50
|
||||||
|
|
||||||
|
from types import ClassMethodDescriptorType
|
||||||
|
from jira import JIRA, Issue, Comment, exceptions
|
||||||
|
from atlassian import Confluence
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
import re
|
||||||
|
import csv
|
||||||
|
import xlsxwriter
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def get_priority(priority):
|
||||||
|
match priority:
|
||||||
|
case "GreenMVP":
|
||||||
|
return "Highest"
|
||||||
|
case _:
|
||||||
|
return "Medium"
|
||||||
|
|
||||||
|
def get_assignee():
|
||||||
|
return 'raj9002'
|
||||||
|
|
||||||
|
isBuergerfrontend = False
|
||||||
|
if type == 'Dev Backend':
|
||||||
|
#return 'rab9001'
|
||||||
|
return 'reu9001'
|
||||||
|
elif type == 'Dev Frontend':
|
||||||
|
# consider verwaltungsfrontend or bürgerfrontend
|
||||||
|
|
||||||
|
existingComponents = []
|
||||||
|
for component in issue.fields.components:
|
||||||
|
if component.name == "Bürgerfrontend":
|
||||||
|
isBuergerfrontend = True
|
||||||
|
|
||||||
|
if isBuergerfrontend:
|
||||||
|
return 'reu9001'
|
||||||
|
else:
|
||||||
|
return 'thh9001'
|
||||||
|
|
||||||
|
elif type[:14] == 'Test-Anpassung':
|
||||||
|
return 'ada9001'
|
||||||
|
|
||||||
|
def create_epic(use_case, priority):
|
||||||
|
escaped = use_case.replace('"','\\"')
|
||||||
|
|
||||||
|
epic = jira_server.search_issues('project = 19404 AND summary ~ \"{}\"'.format(escaped))
|
||||||
|
if len(epic) == 0:
|
||||||
|
issue_type = 'Epic'
|
||||||
|
assignee = get_assignee()
|
||||||
|
issue_dict = {
|
||||||
|
'project': {'id': 19404},
|
||||||
|
'summary': escaped,
|
||||||
|
'description': escaped,
|
||||||
|
'issuetype': {'name': issue_type},
|
||||||
|
'priority': {'name': get_priority(priority)},
|
||||||
|
'customfield_10019': escaped,
|
||||||
|
#'parent': {'key': parent},
|
||||||
|
#'labels': issue.fields.labels,
|
||||||
|
#'customfield_10022' - Story Points
|
||||||
|
#'timetracking': {'originalEstimate': "{}d".format(subtask_sp)},
|
||||||
|
'assignee': {'name': assignee}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
new_issue = jira_server.create_issue(fields=issue_dict)
|
||||||
|
except:
|
||||||
|
print("Something")
|
||||||
|
|
||||||
|
# consider components
|
||||||
|
#existingComponents = []
|
||||||
|
#for component in issue.fields.components:
|
||||||
|
# existingComponents.append({"name" : component.name})
|
||||||
|
#new_issue.update(fields={"components": existingComponents})
|
||||||
|
#new_issue.update(priority={"name": issue.fields.priority.name})
|
||||||
|
|
||||||
|
logging.info('Created Epic {}'.format(new_issue))
|
||||||
|
else:
|
||||||
|
#update
|
||||||
|
epic = jira_server.issue(epic[0])
|
||||||
|
logging.info('Skip creating Epic {} as it already exists'.format(epic.key))
|
||||||
|
|
||||||
|
logging.basicConfig(filename='epic_generator.log',
|
||||||
|
filemode='a',
|
||||||
|
format='%(asctime)s - %(levelname)s: %(message)s',
|
||||||
|
level=logging.INFO,
|
||||||
|
encoding='utf-8')
|
||||||
|
|
||||||
|
jira_host = "https://jira.wien.gv.at/"
|
||||||
|
confluence_host = "https://confluence.wien.gv.at/"
|
||||||
|
|
||||||
|
jira_pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD"
|
||||||
|
confluence_pat = "MDI3ODU2NzkzMTAyOsPz47kjJvVdJa1bLpen3W5sB6S5"
|
||||||
|
sprint = 57
|
||||||
|
|
||||||
|
headers = JIRA.DEFAULT_OPTIONS["headers"].copy()
|
||||||
|
headers["Authorization"] = f"Bearer {jira_pat}"
|
||||||
|
|
||||||
|
jira_server = JIRA(server=jira_host, options={"headers": headers})
|
||||||
|
|
||||||
|
s = requests.Session()
|
||||||
|
s.headers['Authorization'] = f"Bearer {confluence_pat}"
|
||||||
|
confluence = Confluence(url=confluence_host, session=s)
|
||||||
|
|
||||||
|
page_id = 215812836
|
||||||
|
page = confluence.get_page_by_id(page_id, expand="body.storage", status=None, version=None)
|
||||||
|
body = page["body"]["storage"]["value"]
|
||||||
|
|
||||||
|
tables_raw = [[[cell.text for cell in row("th") + row("td")]
|
||||||
|
for row in table("tr")]
|
||||||
|
for table in BeautifulSoup(body, features="lxml")("table")]
|
||||||
|
|
||||||
|
logging.info('Creating epics out of confluence description')
|
||||||
|
#xsp = lambda s: s or 0
|
||||||
|
|
||||||
|
tables_df = [pd.DataFrame(table) for table in tables_raw]
|
||||||
|
for table_df in tables_df:
|
||||||
|
for index in table_df.index:
|
||||||
|
if index >= 11 and index <= 12:
|
||||||
|
create_epic(use_case=table_df[1][index],
|
||||||
|
priority=table_df[2][index])
|
||||||
|
|
||||||
|
issue: Issue
|
||||||
|
for issue in issues_Jql:
|
||||||
|
logging.info('Working on {}'.format(issue))
|
||||||
|
sp_sum = 0
|
||||||
|
|
||||||
|
# only Issues with empty (or 0) story-points
|
||||||
|
sp = 0
|
||||||
|
if hasattr(issue.fields, 'customfield_10022'):
|
||||||
|
sp = xsp(issue.fields.customfield_10022)
|
||||||
|
|
||||||
|
if sp != 0:
|
||||||
|
logging.info('Ignoring issue {} as there are Story-Points already omitted'.format(issue))
|
||||||
|
else:
|
||||||
|
single_issue = jira_server.issue(issue.key)
|
||||||
|
comments = single_issue.fields.comment.comments
|
||||||
|
|
||||||
|
# Find all comments made by me on this issue.
|
||||||
|
for comment in comments:
|
||||||
|
comment_lines = comment.body.splitlines()
|
||||||
|
|
||||||
|
if len(comment_lines) == 5:
|
||||||
|
if comment_lines[0][:5] == "Dev B":
|
||||||
|
for comment_line in comment_lines:
|
||||||
|
if comment_line != "":
|
||||||
|
# pos = comment_lines[2].find('NN')
|
||||||
|
comment_line_parts = comment_line.split(':')
|
||||||
|
|
||||||
|
# if 1. Test* != NN create a sub-task (if not existing)
|
||||||
|
if len(comment_line_parts) == 2:
|
||||||
|
if comment_line_parts[1] != u'\xa0': # empty string
|
||||||
|
if (comment_line_parts[1][-2:] != 'NN') and (comment_line_parts[1][-1:] != '-'): # NN means NotNecessary
|
||||||
|
subtask_ph = float(comment_line_parts[1]) * 0.5
|
||||||
|
if comment_line_parts[0][:15] != "Test-Ausführung":
|
||||||
|
create_subtask(issue,
|
||||||
|
issue.key,
|
||||||
|
comment_line_parts[0],
|
||||||
|
subtask_ph)
|
||||||
|
sp_sum += subtask_ph / 0.5
|
||||||
|
|
||||||
|
# find corresponding sub-task
|
||||||
|
# issuelinks.inwardIssue
|
||||||
|
try:
|
||||||
|
if sp != sp_sum:
|
||||||
|
issue.update(fields={'customfield_10022': sp_sum})
|
||||||
|
logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, sp_sum))
|
||||||
|
|
||||||
|
except exceptions.JIRAError:
|
||||||
|
logging.error("Issue {} has no (visible) Story Points-attribute".format(issue.key))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
logging.info('Done {}'.format(issue.key))
|
||||||
|
|
||||||
|
#print('{}: {}, SP: {}'.format(issue.key, issue.fields.summary, sp))
|
||||||
|
|
||||||
|
#print('Summe {}: {}'.format(assignee, sp_sum))
|
||||||
|
|
||||||
|
#print(issues_Jql)
|
||||||
|
# changelog = issues_Jql.
|
||||||
|
# count = 0
|
||||||
|
# for history in changelog.histories:
|
||||||
|
# for item in history.items:
|
||||||
|
# if item.field == 'status':
|
||||||
|
# if item.toString == "Reopened":
|
||||||
|
# count = count + 1
|
||||||
|
# print(count)
|
||||||
|
|
||||||
+64
-50
@@ -8,55 +8,60 @@ import csv
|
|||||||
import xlsxwriter
|
import xlsxwriter
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
def get_assignee(type):
|
def get_assignee(type, issue):
|
||||||
|
isBuergerfrontend = False
|
||||||
if type == 'Dev Backend':
|
if type == 'Dev Backend':
|
||||||
#return 'rab9001'
|
|
||||||
return 'reu9001'
|
return 'reu9001'
|
||||||
elif type == 'Dev Frontend':
|
elif type == 'Dev Frontend':
|
||||||
# consider verwaltungsfrontend or bürgerfrontend
|
for component in issue.fields.components:
|
||||||
|
isBuergerfrontend = component.name == "Bürgerfrontend"
|
||||||
|
if isBuergerfrontend:
|
||||||
|
return 'reu9001'
|
||||||
return 'thh9001'
|
return 'thh9001'
|
||||||
# return 'reu9001'
|
elif type.startswith('Test-Anpassung'):
|
||||||
elif type[:14] == 'Test-Anpassung':
|
|
||||||
return 'ada9001'
|
return 'ada9001'
|
||||||
|
|
||||||
def create_subtask(issue, parent, type, subtask_sp):
|
def create_subtask(issue, parent, type, subtask_sp, estimation_remark):
|
||||||
escaped = issue.fields.summary.replace('"','\\"')
|
escaped = issue.fields.summary.replace('"', r'\"')
|
||||||
print('summary ~ \"{}\"'.format(escaped))
|
logging.info(f'summary ~ \"{escaped}\"')
|
||||||
|
|
||||||
subtask = jira_server.search_issues('summary ~ \"{} {}\"'.format(type, escaped))
|
|
||||||
if len(subtask) == 0:
|
|
||||||
#right = (comment_line.find(':')+2)-len(comment_line)
|
|
||||||
#subtask_sp = float(comment_line[right:])
|
|
||||||
|
|
||||||
# issuetype of testcase-creation/modification should be 'Test'
|
|
||||||
# if type == 'Test-Anpassung':
|
|
||||||
issue_type = 'Sub-task'
|
|
||||||
|
|
||||||
assignee = get_assignee(type)
|
try:
|
||||||
issue_dict = {
|
subtask = jira_server.search_issues(f'summary ~ \"{type} {escaped}\"')
|
||||||
'project': {'id': 12501},
|
if subtask:
|
||||||
'summary': "[{}] {}".format(type, escaped),
|
raise exceptions.JIRAError
|
||||||
'description': issue.fields.description,
|
except exceptions.JIRAError:
|
||||||
'issuetype': {'name': issue_type},
|
logging.info(f'Skip creating Sub-Task {type} for {issue} as it already exists')
|
||||||
'priority': {'name': issue.fields.priority.name},
|
return
|
||||||
'parent': {'key': parent},
|
|
||||||
'labels': issue.fields.labels,
|
|
||||||
#'customfield_10022' - Story Points
|
|
||||||
'timetracking': {'originalEstimate': "{}d".format(subtask_sp)},
|
|
||||||
'assignee': {'name': assignee}
|
|
||||||
}
|
|
||||||
new_issue = jira_server.create_issue(fields=issue_dict)
|
|
||||||
|
|
||||||
# consider components
|
issue_type = 'Sub-task'
|
||||||
existingComponents = []
|
|
||||||
for component in issue.fields.components:
|
|
||||||
existingComponents.append({"name" : component.name})
|
|
||||||
new_issue.update(fields={"components": existingComponents})
|
|
||||||
#new_issue.update(priority={"name": issue.fields.priority.name})
|
|
||||||
|
|
||||||
logging.info('Created Sub-Task {}'.format(new_issue))
|
assignee = get_assignee(type, issue)
|
||||||
else:
|
description = 's. Story'
|
||||||
logging.info('Skip creating Sub-Task {} for {} as it already exists'.format(type, issue))
|
if estimation_remark != "":
|
||||||
|
description += "\nEstimation remark: {}".format(estimation_remark)
|
||||||
|
|
||||||
|
issue_dict = {
|
||||||
|
'project': {'id': issue.fields.project.id},
|
||||||
|
'summary': "[{}] {}".format(type, escaped),
|
||||||
|
'description': description, # issue.fields.description,
|
||||||
|
'issuetype': {'name': issue_type},
|
||||||
|
'priority': {'name': issue.fields.priority.name},
|
||||||
|
'parent': {'key': parent},
|
||||||
|
'labels': issue.fields.labels,
|
||||||
|
#'customfield_10022' - Story Points
|
||||||
|
'timetracking': {'originalEstimate': "{}d".format(subtask_sp)},
|
||||||
|
'assignee': {'name': assignee}
|
||||||
|
}
|
||||||
|
new_issue = jira_server.create_issue(fields=issue_dict)
|
||||||
|
|
||||||
|
# consider components
|
||||||
|
existingComponents = []
|
||||||
|
for component in issue.fields.components:
|
||||||
|
existingComponents.append({"name" : component.name})
|
||||||
|
new_issue.update(fields={"components": existingComponents})
|
||||||
|
#new_issue.update(priority={"name": issue.fields.priority.name})
|
||||||
|
|
||||||
|
logging.info('Created Sub-Task {}'.format(new_issue))
|
||||||
|
|
||||||
logging.basicConfig(filename='sub_task_generator.log',
|
logging.basicConfig(filename='sub_task_generator.log',
|
||||||
filemode='a',
|
filemode='a',
|
||||||
@@ -66,15 +71,15 @@ logging.basicConfig(filename='sub_task_generator.log',
|
|||||||
|
|
||||||
host = "https://jira.wien.gv.at/"
|
host = "https://jira.wien.gv.at/"
|
||||||
pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD"
|
pat = "OTAyMTM3MjY0MjE0Ovz6bu0ti1TlEdSegmmiWiJ2wGeD"
|
||||||
sprint = 46
|
sprint = 4
|
||||||
|
|
||||||
headers = JIRA.DEFAULT_OPTIONS["headers"].copy()
|
headers = JIRA.DEFAULT_OPTIONS["headers"].copy()
|
||||||
headers["Authorization"] = f"Bearer {pat}"
|
headers["Authorization"] = f"Bearer {pat}"
|
||||||
jira_server = JIRA(server=host, options={"headers": headers})
|
jira_server = JIRA(server=host, options={"headers": headers})
|
||||||
|
|
||||||
# use filter or single issue
|
# use filter or single issue
|
||||||
issues_Jql = jira_server.search_issues('filter=20406') #, expand='changelog')
|
issues_Jql = jira_server.search_issues('filter=22140') #, expand='changelog')
|
||||||
#issues_Jql = jira_server.search_issues('key in (WRGWR-1558)') #, expand='changelog')
|
#issues_Jql = jira_server.search_issues('key in (DKARB-34)') #, expand='changelog')
|
||||||
#issues_Jql = jira_server.search_issues('labels in (Refinement11012022) AND type not in (Bug) AND status not in (Done) and ("Story Points" is EMPTY or "Story Points" = 0)') #, expand='changelog')
|
#issues_Jql = jira_server.search_issues('labels in (Refinement11012022) AND type not in (Bug) AND status not in (Done) and ("Story Points" is EMPTY or "Story Points" = 0)') #, expand='changelog')
|
||||||
|
|
||||||
logging.info('Creating sub-tasks out of estimation of pre-planning sprint {}'.format(sprint))
|
logging.info('Creating sub-tasks out of estimation of pre-planning sprint {}'.format(sprint))
|
||||||
@@ -110,20 +115,29 @@ for issue in issues_Jql:
|
|||||||
# if 1. Test* != NN create a sub-task (if not existing)
|
# if 1. Test* != NN create a sub-task (if not existing)
|
||||||
if len(comment_line_parts) == 2:
|
if len(comment_line_parts) == 2:
|
||||||
if comment_line_parts[1] != u'\xa0': # empty string
|
if comment_line_parts[1] != u'\xa0': # empty string
|
||||||
if comment_line_parts[1][-2:] != 'NN': # NN means NotNecessary
|
if (comment_line_parts[1][-2:] != 'NN') and (comment_line_parts[1][-1:] != '-'): # NN means NotNecessary
|
||||||
subtask_ph = float(comment_line_parts[1]) * 0.5
|
comment_line_sub_parts = comment_line_parts[1].split(' (')
|
||||||
|
if len(comment_line_sub_parts)>1:
|
||||||
|
subtask_ph = float(comment_line_sub_parts[0]) * 0.5
|
||||||
|
estimation_remark = comment_line_sub_parts[1]
|
||||||
|
else:
|
||||||
|
subtask_ph = float(comment_line_parts[1]) * 0.5
|
||||||
|
estimation_remark = ""
|
||||||
|
|
||||||
if comment_line_parts[0][:15] != "Test-Ausführung":
|
if comment_line_parts[0][:15] != "Test-Ausführung":
|
||||||
create_subtask(issue,
|
create_subtask(issue,
|
||||||
issue.key,
|
issue.key,
|
||||||
comment_line_parts[0],
|
comment_line_parts[0],
|
||||||
subtask_ph)
|
subtask_ph,
|
||||||
|
estimation_remark)
|
||||||
sp_sum += subtask_ph / 0.5
|
sp_sum += subtask_ph / 0.5
|
||||||
|
|
||||||
# find corresponding sub-task
|
# find corresponding sub-task
|
||||||
# issuelinks.inwardIssue
|
# issuelinks.inwardIssue
|
||||||
try:
|
try:
|
||||||
issue.update(fields={'customfield_10022': sp_sum})
|
if sp != sp_sum:
|
||||||
logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, sp_sum))
|
issue.update(fields={'customfield_10022': sp_sum})
|
||||||
|
logging.info("Estimated story-points changed to Sum(Sub-Tasks story-points): {}".format(sp_sum, sp_sum))
|
||||||
|
|
||||||
except exceptions.JIRAError:
|
except exceptions.JIRAError:
|
||||||
logging.error("Issue {} has no (visible) Story Points-attribute".format(issue.key))
|
logging.error("Issue {} has no (visible) Story Points-attribute".format(issue.key))
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from remedy_py.RemedyAPIClient import RemedyClient
|
||||||
|
|
||||||
|
client = RemedyClient("smartitm01-prod.wienkav.at/", "zim9003", "3S1punk08_111$", port=None, verify=False)
|
||||||
|
|
||||||
|
print(client)
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
||||||
|
# Created by: Brent Goodman
|
||||||
|
# Date: April 07, 2018
|
||||||
|
# Comments: Uses the Python Requests library to do REST API calls to v9 ARSystem Enviornments
|
||||||
|
# Alot of the information is hard-coded, but easy to see how to accomodate other values
|
||||||
|
# etc. All the functions have been tested and work in my new enviornment. All the code
|
||||||
|
# makes use of the "AP-Sample:Restaurant" form for testing. The 'params=' has some
|
||||||
|
# catches that need to be watched out for. I have documented comments through out.
|
||||||
|
# Very limited error checking has been implemented in this version of the code.
|
||||||
|
# Note that C# is my goto language. I only use python for development work on
|
||||||
|
# micro-controllers, so the code I provide may not be the most clean/effecive way
|
||||||
|
# of doing things, but it does get the results needed.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
# Method: GET, POST, PUT, DELET
|
||||||
|
# URL: http://<>/api
|
||||||
|
# Authentication: Basic HTTP, OAuth, none
|
||||||
|
# Custom Headers: Content-Type: application/json
|
||||||
|
# Request Body:
|
||||||
|
|
||||||
|
# Response Status Codes: 200 OK, 201 Created, 500 Internal Error
|
||||||
|
|
||||||
|
# Way to find fields that can contain data
|
||||||
|
# select * from field where schemaid = (select schemaid from arschema where name ='Your Form Name') and foption <=3 order by fieldid;
|
||||||
|
|
||||||
|
# Setup the server name to be used by all the URL's
|
||||||
|
bg_ServerName = "smartitm01-prod.wienkav.at/smartit/app"
|
||||||
|
bg_UserID = "zim9003"
|
||||||
|
bg_Password = "3S1punk08_111$"
|
||||||
|
|
||||||
|
|
||||||
|
def doLogin():
|
||||||
|
myToken = ""
|
||||||
|
bgReturnVal = -1
|
||||||
|
|
||||||
|
url = 'https://' + bg_ServerName + '/api/jwt/login'
|
||||||
|
|
||||||
|
payload = {'username': bg_UserID, 'password': bg_Password}
|
||||||
|
headers = {'content-type': 'application/x-www-form-urlencoded'}
|
||||||
|
|
||||||
|
r = requests.post(url, data=payload, headers=headers, verify=False)
|
||||||
|
|
||||||
|
if r.status_code == 200:
|
||||||
|
myToken = r.text
|
||||||
|
bgReturnVal = 1
|
||||||
|
else:
|
||||||
|
print("Failure...")
|
||||||
|
print("Status Code: " + str(r.status_code))
|
||||||
|
bgReturnVal = -1
|
||||||
|
|
||||||
|
myToken = r.text
|
||||||
|
return(bgReturnVal, myToken)
|
||||||
|
|
||||||
|
def doLogout(bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/jwt/logout'
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token}
|
||||||
|
myR = requests.post(myURL, headers=myHeaders, verify=False)
|
||||||
|
print("")
|
||||||
|
print("Logged out with a status of: {}".format(myR.status_code))
|
||||||
|
|
||||||
|
# Pull all data back from form
|
||||||
|
def doPullData(bg_FormName, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token}
|
||||||
|
|
||||||
|
# Pull back all data in the form
|
||||||
|
myR = requests.get(url = myURL, headers = myHeaders, verify=False)
|
||||||
|
|
||||||
|
print("URL Used:")
|
||||||
|
print(myR.url)
|
||||||
|
print("")
|
||||||
|
|
||||||
|
if(myR.status_code != 200):
|
||||||
|
print('Status code:', myR.status_code)
|
||||||
|
print('Headers:', myR.headers)
|
||||||
|
print('Error Response:', myR.json())
|
||||||
|
return(-1)
|
||||||
|
|
||||||
|
bgData = myR.json()
|
||||||
|
|
||||||
|
# bgData contains a dictionay of two items:
|
||||||
|
# entries
|
||||||
|
# _links
|
||||||
|
|
||||||
|
# Copy the dictionary 'entries' into variable
|
||||||
|
avar = bgData['entries']
|
||||||
|
|
||||||
|
# len(avar) will tell us the number of returned records
|
||||||
|
|
||||||
|
# Loop the number of records returned
|
||||||
|
for x in range(len(avar)):
|
||||||
|
# Grab the dictionary of 'values'
|
||||||
|
myVals = avar[x]['values']
|
||||||
|
# Print out the key/value pair
|
||||||
|
for keys,values in myVals.items():
|
||||||
|
print('{} => {}'.format(keys,values))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
return(len(avar))
|
||||||
|
|
||||||
|
# Pull all records back from form, but limited field list
|
||||||
|
def doPullData_LimitedFields(bg_FormName, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token}
|
||||||
|
|
||||||
|
# Setup the list of fields only to return
|
||||||
|
# *** BMC REST API unable to handle this and I consider it a defect on their side
|
||||||
|
# Their documentation is incorrect
|
||||||
|
#fieldList_params = {'fields': "values(Restaurant,Status)"}
|
||||||
|
|
||||||
|
# Note that BMC documentation is wrong on this. The REST API on their
|
||||||
|
# end is not able to decode the FIELDS, LIMIT, or SORT.
|
||||||
|
# These must be appeneded to the URL. Only the Query can be urlencoded
|
||||||
|
# and put into the 'params=' variable
|
||||||
|
# *** Looks like everything inside of the ( ) needs to be encoded if any non
|
||||||
|
# URL safe characters are used. Still not able to push this into the 'params='
|
||||||
|
# variable as the "values()" also gets encoded. The brackes must not be encoded
|
||||||
|
# Javascript/Java may handle the pramameters a little differently.
|
||||||
|
bgFields = "Restaurant,Status"
|
||||||
|
bgTmp = myURL + "?fields=values(" + bgFields + ")"
|
||||||
|
|
||||||
|
# Setup the qualfication. This automatically gets encoded via the 'params=' variable
|
||||||
|
bgQuery = "'Restaurant' LIKE \"Test%\" AND 'Status' = \"Proposed\""
|
||||||
|
|
||||||
|
# Setup the search parameter to pass
|
||||||
|
search_params = {'q': bgQuery}
|
||||||
|
|
||||||
|
# Pull back all data in the form (The 'params=' will automcatially encode its contents)
|
||||||
|
#myR = requests.get(url = myURL, headers = myHeaders, params=fieldList_params, verify=False)
|
||||||
|
#myR = requests.get(url = myURL, headers = myHeaders, verify=False)
|
||||||
|
|
||||||
|
# Using a modified version of the URL + Fields restriction
|
||||||
|
myR = requests.get(url = bgTmp, headers = myHeaders, params=search_params, verify=False)
|
||||||
|
|
||||||
|
print("URL Used:")
|
||||||
|
print(myR.url)
|
||||||
|
print("")
|
||||||
|
|
||||||
|
if(myR.status_code != 200):
|
||||||
|
print('Status code:', myR.status_code)
|
||||||
|
print('Headers:', myR.headers)
|
||||||
|
print('Error Response:', myR.json())
|
||||||
|
return(-1)
|
||||||
|
|
||||||
|
bgData = myR.json()
|
||||||
|
|
||||||
|
#print(bgData)
|
||||||
|
# bgData contains a dictionay of two items:
|
||||||
|
# entries
|
||||||
|
# _links
|
||||||
|
|
||||||
|
# Copy the dictionary 'entries' into variable
|
||||||
|
avar = bgData['entries']
|
||||||
|
|
||||||
|
# len(avar) will tell us the number of returned records
|
||||||
|
|
||||||
|
# Loop the number of records returned
|
||||||
|
for x in range(len(avar)):
|
||||||
|
# Grab the dictionary of 'values'
|
||||||
|
myVals = avar[x]['values']
|
||||||
|
# Print out the key/value pair
|
||||||
|
for keys,values in myVals.items():
|
||||||
|
print('{} => {}'.format(keys,values))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
return(len(avar))
|
||||||
|
|
||||||
|
def doPullSearchData(bg_FormName, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token}
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName
|
||||||
|
|
||||||
|
# Enter in your search criteria as you would in the Mid-tier advanced search
|
||||||
|
# Dont forget to escape out your quotes...
|
||||||
|
#bgQuery = "'Restaurant' = \"Test Snack Hut\""
|
||||||
|
#bgQuery = "'Restaurant' LIKE \"Test%\""
|
||||||
|
bgQuery = "'Restaurant' LIKE \"Test%\" AND 'Status' = \"Proposed\""
|
||||||
|
|
||||||
|
# Setup the search parameter to pass
|
||||||
|
search_params = {'q': bgQuery}
|
||||||
|
|
||||||
|
myR = requests.get(url=myURL, headers=myHeaders, verify=False, params=search_params)
|
||||||
|
|
||||||
|
print("URL Used:")
|
||||||
|
print(myR.url)
|
||||||
|
print("")
|
||||||
|
|
||||||
|
if(myR.status_code != 200):
|
||||||
|
print('Status code:', myR.status_code)
|
||||||
|
print('Headers:', myR.headers)
|
||||||
|
print('Error Response:', myR.json())
|
||||||
|
return(-1)
|
||||||
|
|
||||||
|
bgData = myR.json()
|
||||||
|
|
||||||
|
# Copy the dictionary 'entries' into variable
|
||||||
|
avar = bgData['entries']
|
||||||
|
|
||||||
|
# len(avar) will tell us the number of returned records
|
||||||
|
|
||||||
|
# Loop the number of records returned
|
||||||
|
for x in range(len(avar)):
|
||||||
|
# Grab the dictionary of 'values'
|
||||||
|
myVals = avar[x]['values']
|
||||||
|
# Print out the key/value pair
|
||||||
|
for keys,values in myVals.items():
|
||||||
|
print('{} => {}'.format(keys,values))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
return(len(avar))
|
||||||
|
|
||||||
|
# Pull all data back from form
|
||||||
|
def doPullSingleRequestIDData(bg_FormName, bg_RequestID, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName + '/' + bg_RequestID
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token}
|
||||||
|
|
||||||
|
# Pull back all data in the form
|
||||||
|
myR = requests.get(url = myURL, headers = myHeaders, verify=False)
|
||||||
|
|
||||||
|
print("URL Used:")
|
||||||
|
print(myR.url)
|
||||||
|
print("")
|
||||||
|
|
||||||
|
if(myR.status_code != 200):
|
||||||
|
print('Status code:', myR.status_code)
|
||||||
|
print('Headers:', myR.headers)
|
||||||
|
print('Error Response:', myR.json())
|
||||||
|
return(-1)
|
||||||
|
|
||||||
|
bgData = myR.json()
|
||||||
|
|
||||||
|
# bgData contains a dictionay of two items:
|
||||||
|
# entries
|
||||||
|
# _links
|
||||||
|
|
||||||
|
#print(bgData)
|
||||||
|
|
||||||
|
# Copy the dictionary 'entries' into variable
|
||||||
|
avar = bgData['values']
|
||||||
|
|
||||||
|
# Print out the key/value pair
|
||||||
|
for keys,values in avar.items():
|
||||||
|
print('{} => {}'.format(keys,values))
|
||||||
|
|
||||||
|
return(len(avar))
|
||||||
|
|
||||||
|
|
||||||
|
# Create a record in form
|
||||||
|
def doCreateRecord(bg_FormName, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token, 'Content-Type': 'application/json'}
|
||||||
|
|
||||||
|
# Setting the data= like this will not work. Must be in the raw JSON format
|
||||||
|
#payload = {'Restaurant':'Test Cheese House','Status':'Active','Average Cost/Person':12.99,'Assigned To':'Bob'}
|
||||||
|
|
||||||
|
# Format of the data= information must use the following template:
|
||||||
|
# {
|
||||||
|
# "values":{
|
||||||
|
# "Restaurant":"Test MacEnCheese", <-- Start of the fields list to set
|
||||||
|
# "Status":"Active",
|
||||||
|
# "Average Cost/Person":2.99,
|
||||||
|
# "Assigned To":"Bob" <-- End of the fields list to set
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
payload = '{ \"values\":{\"Restaurant\":\"Test Pizza World\",\"Status\":\"Active\",\"Average Cost/Person\":2.99,\"Assigned To\":\"Bob\"} }'
|
||||||
|
|
||||||
|
# Pull back all data in the form
|
||||||
|
myR = requests.post(url = myURL, data=payload, headers = myHeaders, verify=False)
|
||||||
|
|
||||||
|
if(myR.status_code != 201):
|
||||||
|
print('Status code:', myR.status_code)
|
||||||
|
print('Headers:', myR.headers)
|
||||||
|
print('Error Response:', myR.json())
|
||||||
|
return(-1)
|
||||||
|
|
||||||
|
print("URL to new record:")
|
||||||
|
print(myR.headers['location'])
|
||||||
|
print("")
|
||||||
|
|
||||||
|
return(1)
|
||||||
|
|
||||||
|
# Modify an Entry
|
||||||
|
def doModifySingleRequestIDData(bg_FormName, bg_RequestID, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName + '/' + bg_RequestID
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token, 'Content-Type':'application/json'}
|
||||||
|
|
||||||
|
payload = '{ \"values\":{\"Status\":\"Inactive\",\"Average Cost/Person\":3.99,\"Assigned To\":\"Rick\"} }'
|
||||||
|
# Pull back all data in the form
|
||||||
|
myR = requests.put(url = myURL, headers = myHeaders, data=payload, verify=False)
|
||||||
|
|
||||||
|
print("URL Used:")
|
||||||
|
print(myR.url)
|
||||||
|
print("")
|
||||||
|
print("Status of Update: {}".format(myR.status_code))
|
||||||
|
|
||||||
|
# 204 = sucessful updateTime
|
||||||
|
# 412 = unmodified (same data was pushed)
|
||||||
|
if(myR.status_code != 204):
|
||||||
|
if(myR.status_code != 412):
|
||||||
|
# 403 = Forbidden
|
||||||
|
# 404 = Form does not exist
|
||||||
|
print('Status code:', myR.status_code)
|
||||||
|
print("")
|
||||||
|
print('Headers:', myR.headers)
|
||||||
|
print("")
|
||||||
|
print('Error Response:', myR.json())
|
||||||
|
return(-1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def getFormDefinition(bg_FormName, bg_Token):
|
||||||
|
bgTokenType = 'AR-JWT'
|
||||||
|
myURL = 'https://' + bg_ServerName + '/api/arsys/v1/entry/' + bg_FormName
|
||||||
|
myHeaders = {'Authorization': bgTokenType + ' ' + bg_Token}
|
||||||
|
|
||||||
|
myR = requests.options(url = myURL, headers = myHeaders, verify=False)
|
||||||
|
|
||||||
|
print("URL Used:")
|
||||||
|
print(myR.url)
|
||||||
|
print("")
|
||||||
|
print("Status of call: {}".format(myR.status_code))
|
||||||
|
|
||||||
|
bgData = myR.json()
|
||||||
|
|
||||||
|
# Print the raw data returned
|
||||||
|
#print(bgData)
|
||||||
|
|
||||||
|
# Drill down and only pull out the field information
|
||||||
|
avar = bgData['properties']['values']['properties']
|
||||||
|
|
||||||
|
# Print the new raw data
|
||||||
|
#print(avar)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("-----Key Value pairs-----")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
# Loop through all the fields returned
|
||||||
|
for key in sorted(avar.keys()):
|
||||||
|
print("Key: {}".format(key))
|
||||||
|
|
||||||
|
# Pull out a field to look at
|
||||||
|
tmp = avar[key]
|
||||||
|
|
||||||
|
# Print out the 'type' keys data. Exists for all fields
|
||||||
|
print("Type: {}".format(tmp['type']))
|
||||||
|
|
||||||
|
# If the key exists, print it out
|
||||||
|
if 'maxLength' in tmp:
|
||||||
|
print("Length: {}".format(tmp['maxLength']))
|
||||||
|
|
||||||
|
if 'format' in tmp:
|
||||||
|
print("Format: {}".format(tmp['format']))
|
||||||
|
|
||||||
|
if 'required' in tmp:
|
||||||
|
print("Required: {}".format(tmp['required']))
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("-----------------------")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Login and get token
|
||||||
|
bgReturnVal, bgToken = doLogin()
|
||||||
|
|
||||||
|
# Only continue if we logged in successfully
|
||||||
|
if bgReturnVal == 1:
|
||||||
|
|
||||||
|
# Pull all records
|
||||||
|
#bgResult = doPullData("AP-Sample:Restaurant", bgToken)
|
||||||
|
#if(bgResult == -1):
|
||||||
|
# print("An error occured...")
|
||||||
|
#else:
|
||||||
|
# print("Found {} record(s) in our last query".format(bgResult))
|
||||||
|
|
||||||
|
# Pull all records back, but only certain fields
|
||||||
|
#bgResult = doPullData_LimitedFields("AP-Sample:Restaurant", bgToken)
|
||||||
|
|
||||||
|
# Pull records based on hard coded query
|
||||||
|
#bgResult = doPullSearchData("AP-Sample:Restaurant", bgToken)
|
||||||
|
|
||||||
|
# Pull record back based on its RequestID. This is a standard format
|
||||||
|
#doPullSingleRequestIDData("AP-Sample:Restaurant", "000000000000105", bgToken)
|
||||||
|
|
||||||
|
# Create a new recodrs in the form (hard coded values used)
|
||||||
|
#bgResult = doCreateRecord("AP-Sample:Restaurant", bgToken)
|
||||||
|
|
||||||
|
# Modify the record based on its RequestID. This is a standard format
|
||||||
|
#doModifySingleRequestIDData("AP-Sample:Restaurant", "000000000000105", bgToken)
|
||||||
|
|
||||||
|
# Get definitions of all the fields on form
|
||||||
|
getFormDefinition("AP-Sample:Restaurant", bgToken)
|
||||||
|
|
||||||
|
|
||||||
|
# Logout
|
||||||
|
doLogout(bgToken)
|
||||||
|
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user