Skip to content

Commit 3b23468

Browse files
authored
Merge branch 'main' into Incident-Escalation-and-Notifications
2 parents df5859a + 7cf9aaf commit 3b23468

File tree

122 files changed

+2254
-9
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+2254
-9
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Use Case: Find the Total Number of Records in a Table Using the ATF Step "Run Server Side Script"
2+
3+
Using existing ATF steps (without scripting), it is very difficult to find the record count of a table.
4+
5+
By using the ATF test step "Run Server Side Script" with a simple script, we can count the records and also log/pass the count to the next ATF step if required.
6+
7+
Steps:
8+
9+
Navigate to Automated Test Framework >> Tests >> Click on New Test.
10+
Give a name to the test and provide a description.
11+
Go to the Test Steps related list and click Add Test Step.
12+
Navigate to Server and choose Run Server Side Script.
13+
Add the script (the script is in the script.js file).
14+
Save the test and run it to see the results.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
(function(outputs, steps, params, stepResult, assertEqual) {
2+
// add test script here
3+
var gr = new GlideAggregate('incident');
4+
gr.addAggregate('COUNT');
5+
gr._query();
6+
if (gr.next()) {
7+
return gr.getAggregate('COUNT'); // pass the step
8+
stepResult.setOutputMessage("Successfully Calculated the Count");
9+
} else {
10+
stepResult.setOutputMessage("Failed to Count");
11+
return false; // fail the step
12+
}
13+
14+
})(outputs, steps, params, stepResult, assertEqual);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Description
2+
The planned lines of the ServiceNow burndown chart do not take holidays into account.
3+
By using this Python snippet, you can create a burndown chart with planned lines that take holidays into account.
4+
The generated burndown chart can also be automatically deployed as an image to Slack and other tools.
5+
6+
## Requirements
7+
OS: Windows/MacOS/Unix
8+
Python: Python3.x
9+
ServiceNow: More than Vancouver
10+
Plugins: Agile Development 2.0 (com.snc.sdlc.agile.2.0) is installed
11+
12+
## Installation
13+
Clone the repository and place the "Burndown Chart" directory in any location.
14+
Execute the following command to create a virtual environment.
15+
<code>python3 -m venv .venv
16+
macOS/Unix: source .venv/bin/activate
17+
Windows: .venv\Scripts\activate
18+
pip install -r requirements.txt
19+
</code>
20+
21+
## Usage
22+
1. Go to the Burndown Chart directory.
23+
2. Prepare the following values ​​according to your environment:
24+
- InstanceName: Your instance name (e.g. dev000000 for PDI)
25+
- Credentials: Instance login information in Base64 (Read permission to the rm_sprint table is required.)
26+
- Sprint Name: Target sprint name from the Sprint[rm_sprint] table
27+
28+
3. Run the command to install the required modules.
29+
<code>pip install -r equirements.txt</code>
30+
31+
5. Run sprint_burndown_chart.py.
32+
<code>python3 sprint_burndown_chart.py INSTANCE_NAME BASE64_ENCODED_STRING(USERID:PASSWORD) SPRINT_NAME</code>
33+
example:
34+
<code>python3 sprint_burndown_chart.py dev209156 YXBpOmpkc0RhajNAZDdKXnNmYQ== Sprint1</code>
35+
36+
When you run it, a burndown chart image like the one shown will be created.
37+
![figure](https://github.com/user-attachments/assets/50d3ffc2-4c66-4f4d-bb69-c2b98763621d)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
jpholiday==0.1.10
2+
matplotlib==3.9.2
3+
numpy==2.0.2
4+
pandas==2.2.3
5+
pytz==2024.2
6+
requests==2.32.3
7+
urllib3==1.26.13
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import argparse
2+
import pprint
3+
import requests
4+
import datetime
5+
import matplotlib.dates as mdates
6+
import pandas as pd
7+
import matplotlib.pyplot as plt
8+
import urllib.request
9+
import urllib.parse
10+
import json
11+
import datetime
12+
from pytz import timezone
13+
14+
# ---- #
15+
# init #
16+
# ---- #
17+
point_dict = {}
18+
total_points = 0
19+
done = 0
20+
undone = 0
21+
parser = argparse.ArgumentParser()
22+
parser.add_argument('instancename')
23+
parser.add_argument('authstring')
24+
parser.add_argument('sprintname')
25+
args = parser.parse_args()
26+
BASIC = 'Basic ' + args.authstring
27+
28+
# ---------- #
29+
# Get Sprint #
30+
# ---------- #
31+
params = {
32+
'sysparm_query': 'short_description=' + args.sprintname
33+
}
34+
param = urllib.parse.urlencode(params)
35+
url = "https://" + args.instancename + ".service-now.com/api/now/table/rm_sprint?" + param
36+
req = urllib.request.Request(url)
37+
req.add_header("authorization", BASIC)
38+
with urllib.request.urlopen(req) as res:
39+
r = res.read().decode("utf-8")
40+
obj = json.loads(r)
41+
# Get the start and end dates of a Sprint
42+
start_date = obj['result'][0]['start_date']
43+
start_date = (datetime.datetime.strptime(start_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=9)).date()
44+
print(start_date)
45+
end_date = obj['result'][0]['end_date']
46+
end_date = (datetime.datetime.strptime(end_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=9)).date()
47+
# Initializing the points array
48+
while start_date <= end_date:
49+
point_dict[str(start_date)] = 0
50+
start_date = start_date + datetime.timedelta(days=1)
51+
# --------- #
52+
# Get Story #
53+
# --------- #
54+
params = {
55+
'sysparm_query': 'sprint.short_descriptionLIKE' + args.sprintname
56+
}
57+
param = urllib.parse.urlencode(params)
58+
url = "https://" + args.instancename + ".service-now.com/api/now/table/rm_story?" + param
59+
req = urllib.request.Request(url)
60+
req.add_header("authorization", BASIC)
61+
with urllib.request.urlopen(req) as res:
62+
r = res.read().decode("utf-8")
63+
obj = json.loads(r)
64+
# Story Loop
65+
for name in obj['result']:
66+
if len(name['story_points']) > 0:
67+
total_points += int(name['story_points'])
68+
if name['closed_at'] != '':
69+
close_date = datetime.datetime.strptime(
70+
name['closed_at'], '%Y-%m-%d %H:%M:%S')
71+
close_date = close_date.date()
72+
if name['state'] == '3':
73+
if str(close_date) in point_dict:
74+
point_dict[str(close_date)] += int(name['story_points'])
75+
else:
76+
point_dict[str(close_date)] = int(name['story_points'])
77+
if name['state'] == '3':
78+
done += int(name['story_points'])
79+
else:
80+
undone += int(name['story_points'])
81+
counta = 0
82+
for i in point_dict.items():
83+
counta += int(i[1])
84+
point_dict[i[0]] = total_points - counta
85+
plt.xkcd()
86+
fig, ax = plt.subplots()
87+
# Creating a performance line
88+
x = []
89+
y = []
90+
plt.ylim(0, total_points + 5)
91+
counta = 0
92+
for key in point_dict.keys():
93+
if datetime.datetime.today() >= datetime.datetime.strptime(key, '%Y-%m-%d'):
94+
x.append(datetime.datetime.strptime(key, '%Y-%m-%d'))
95+
y.append(point_dict[key])
96+
# Holiday determination
97+
DATE = "yyyymmdd"
98+
def isBizDay(DATE):
99+
Date = datetime.date(int(DATE[0:4]), int(DATE[4:6]), int(DATE[6:8]))
100+
if Date.weekday() >= 5:
101+
return 0
102+
else:
103+
return 1
104+
# Get the number of weekdays
105+
total_BizDay = 0
106+
for key in point_dict.keys():
107+
if isBizDay(key.replace('-', '')) == 1:
108+
total_BizDay += 1
109+
# Creating an ideal line
110+
x2 = []
111+
y2 = []
112+
point_dict_len = len(point_dict)
113+
average = total_points / (total_BizDay - 1)
114+
for key in point_dict.keys():
115+
dtm = datetime.datetime.strptime(key, '%Y-%m-%d')
116+
x2.append(dtm)
117+
y2.append(total_points)
118+
# If the next day is a weekday, consume the ideal line.
119+
if isBizDay((dtm + datetime.timedelta(days=1)).strftime("%Y%m%d")) == 1:
120+
total_points -= average
121+
days = mdates.DayLocator()
122+
daysFmt = mdates.DateFormatter('%m/%d')
123+
ax.xaxis.set_major_locator(days)
124+
ax.xaxis.set_major_formatter(daysFmt)
125+
plt.title("" + args.sprintname + " Burndown")
126+
plt.plot(x2, y2, label="Ideal", color='green')
127+
plt.plot(x2, y2, marker='.', markersize=20, color='green')
128+
plt.plot(x, y, label="Actual", color='red')
129+
plt.plot(x, y, marker='.', markersize=20, color='red')
130+
plt.grid()
131+
plt.xlabel("Days")
132+
plt.ylabel("Remaining Effort(pts)")
133+
plt.subplots_adjust(bottom=0.2)
134+
plt.legend()
135+
# Viewing the graph
136+
# plt.show()
137+
# Saving a graph
138+
plt.savefig('figure.png')
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Script to be used to add bookmark for ITIL users. This will help add favorites for SLAs for
2+
- My Group Tasks
3+
- SLAs for My Tasks
4+
- Tasks Assigned to Me
5+
- My approvals
6+
to all ITIL users.
7+
Replace the addQuery value to get the favorites applied from the backend to required audience
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
var jsonFavList = {
2+
"SLA for My Group Tasks": "task_list.do?sysparm_query=assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744&sysparm_first_row=1&sysparm_view=",
3+
"SLA for My Tasks": "task_list.do?sysparm_query=assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe&sysparm_first_row=1&sysparm_view=",
4+
"Tasks Assigned to Me": "task_list.do?sysparm_query=stateNOT INclosed_complete,closed_abandoned^assigned_toDYNAMIC90d1921e5f510100a9ad2572f2b477fe",
5+
"My approvals": "sysapproval_approver_list.do?sysparm_query=approverDYNAMIC90d1921e5f510100a9ad2572f2b477fe&sysparm_first_row=1&sysparm_view="
6+
};
7+
8+
var g = new GlideRecord("sys_user_has_role");
9+
g.addEncodedQuery("role=282bf1fac6112285017366cb5f867469");//considering sys_id for ITIL role is 282bf1fac6112285017366cb5f867469
10+
g.query();
11+
while (g.next()) {
12+
for (var fav in jsonFavList) {
13+
var grBookMark = new GlideRecord("sys_ui_bookmark");
14+
grBookMark.addEncodedQuery("user=" + g.user + "^title=" + fav + "^url=" + jsonFavList[fav]);
15+
grBookMark.query();
16+
if (!grBookMark.next()) {
17+
grBookMark.initialize();
18+
grBookMark.pinned = true;
19+
grBookMark.title = fav;
20+
grBookMark.url = jsonFavList[fav];
21+
grBookMark.user = g.user;
22+
grBookMark.insert();
23+
}
24+
}
25+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Using GlideAggregate function to find out tickets (tasks) with same number. OOB there happens to be a Unique checkbox at dictionary level
2+
and if in case not set to True it might create duplicate numbered tickets.
3+
Script will help find, ticekts if any.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var dpchk = new GlideAggregate('task');
2+
dpchk.groupBy('number');
3+
dpchk.addHaving('COUNT', '>', 1);
4+
dpchk.query();
5+
while(dpchk.next())
6+
{
7+
gs.print(dpchk.number);
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Background Script provides the list of installed plugins, version installed and version available for the upgrade in the instance
2+
3+
Note: We need to set the basic auth credential in order for the script to work on the instance where we are running it.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//Get the instance
2+
var instance_name = gs.getProperty("instance_name");
3+
4+
//build the endpoint
5+
var endPoint = 'https://'+instance_name+'.service-now.com/api/sn_appclient/appmanager/apps?tab_context=installed';
6+
7+
//initialize the RestMessageV2 API.
8+
var request = new sn_ws.RESTMessageV2();
9+
request.setEndpoint(endPoint);
10+
request.setHttpMethod('POST');
11+
12+
//Eg. UserName="admin", Password="admin" for this code sample.
13+
var user = 'admin';
14+
var password = '****';
15+
16+
//set the authentication
17+
request.setBasicAuth(user,password);
18+
19+
//set the request header
20+
request.setRequestHeader("Accept","application/json");
21+
22+
//invoke the API
23+
var response = request.execute();
24+
25+
//Parse the response
26+
var jsonResponse = JSON.parse(response.getBody());
27+
28+
var appsList = jsonResponse.result.apps;
29+
30+
//Print the Header for the response
31+
gs.info("Application name"+" | "+ "Assigned version"+" | " + "Latest version | " + "Hasupdate");
32+
appsList.forEach(function(app){
33+
//Print the plugin details
34+
var hasUpdate = app.update_available == 1 ? "true" : "false";
35+
gs.info(app.name+" | "+ app.assigned_version+" | " + app.latest_version+" | " + hasUpdate);
36+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This snippet outputs a list of module types included in an update set.
2+
By checking the included module types before committing an update set, you can be sure you aren't including any unintended updates.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function getListOfUpdateSetTypes(update_set){
2+
var result = []
3+
var gr = new GlideAggregate('sys_update_xml');
4+
gr.addQuery('update_set', update_set);
5+
gr.groupBy('type');
6+
gr.query();
7+
while(gr.next()){
8+
result.push(gr.type.getDisplayValue())
9+
}
10+
return result;
11+
}
12+
13+
getListOfUpdateSetTypes('update_set_sys_id');
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Replace 'Project Workspace' with the name of the application you want to check
2+
var appName = 'Project Workspace';
3+
4+
var appRecord = new GlideRecord('sys_app');
5+
appRecord.addQuery('name', appName);
6+
appRecord.query();
7+
8+
if (appRecord.next()) {
9+
gs.print("Application Name: " + appName);
10+
gs.print("Current Version: " + appRecord.getValue('version'));
11+
} else {
12+
gs.print("The Application '" + appName + "' is not found.");
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
- Set the appName variable to the exact name of the application you’re checking (in this case as an example, Project Workspace).
2+
- This script queries the Application [sys_app] table for a record with the specified name.
3+
- If the application is found, it retrieves and prints the version. If not, it prints a message stating the application wasn’t found.
4+
- This script will find the version of a specific application and output the version in the Scripts - Background logs.
5+
6+
7+
8+
9+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Creating a background script to print the total count of all tables with a specific filter condition

Background Scripts/GetRecordsFromMultipleTables/script.js

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
var updatedSysIds = [];
2+
var notUpdatedSysIds = [];
3+
4+
var gr = new GlideRecord('< table_name >');
5+
gr.addQuery('< query condition >');
6+
gr.query();
7+
8+
while (gr.next()) {
9+
10+
var relCi = new GlideRecord('cmdb_rel_ci');
11+
relCi.addQuery('child', gr.sys_id);
12+
relCi.addQuery('parent.sys_class_name', '< backend name of the table to which the reference field is referred to >');
13+
relCi.query();
14+
15+
if (relCi.next()) {
16+
// Update the reference field with the referenced table's sys_id
17+
gr.< reference field backend name > = relCi.parent.sys_id;
18+
gr.setWorkflow(false);
19+
gr.update();
20+
updatedSysIds.push(gr.sys_id.toString()); // Add to updated list
21+
}
22+
else {
23+
notUpdatedSysIds.push(gr.sys_id.toString()); // Add to not updated list
24+
}
25+
}
26+
27+
// Print the sys_ids of the records updated and not updated
28+
gs.print("Updated records sys_ids: " + updatedSysIds.join(', '));
29+
gs.print("Not updated records sys_ids: " + notUpdatedSysIds.join(', '));

0 commit comments

Comments
 (0)