browsergym-workarena 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- browsergym/workarena/__init__.py +13 -1
- browsergym/workarena/api/category.py +74 -0
- browsergym/workarena/api/change_request.py +87 -0
- browsergym/workarena/api/computer_asset.py +90 -0
- browsergym/workarena/api/cost_center.py +19 -0
- browsergym/workarena/api/expense_line.py +89 -0
- browsergym/workarena/api/incident.py +45 -0
- browsergym/workarena/api/knowledge.py +29 -0
- browsergym/workarena/api/problem.py +90 -0
- browsergym/workarena/api/report.py +183 -0
- browsergym/workarena/api/requested_items.py +63 -0
- browsergym/workarena/api/user.py +11 -8
- browsergym/workarena/api/utils.py +47 -3
- browsergym/workarena/config.py +21 -1
- browsergym/workarena/data_files/setup_files/forms/expected_incident_form_fields.json +1 -1
- browsergym/workarena/data_files/setup_files/forms/expected_request_item_form_fields.json +1 -0
- browsergym/workarena/data_files/setup_files/knowledge/protocols.json +46 -0
- browsergym/workarena/data_files/setup_files/knowledge/test.html +1 -0
- browsergym/workarena/data_files/setup_files/lists/expected_asset_list_columns.json +2 -24
- browsergym/workarena/data_files/setup_files/lists/expected_change_request_list_columns.json +4 -40
- browsergym/workarena/data_files/setup_files/lists/expected_expense_line_list_columns.json +12 -0
- browsergym/workarena/data_files/setup_files/lists/expected_hardware_list_columns.json +1 -42
- browsergym/workarena/data_files/setup_files/lists/expected_incident_list_columns.json +2 -18
- browsergym/workarena/data_files/setup_files/lists/expected_problem_list_columns.json +12 -0
- browsergym/workarena/data_files/setup_files/lists/expected_requested_items_list_columns.json +12 -0
- browsergym/workarena/data_files/setup_files/lists/expected_service_catalog_list_columns.json +2 -19
- browsergym/workarena/data_files/setup_files/lists/expected_user_list_columns.json +3 -50
- browsergym/workarena/data_files/task_configs/all_menu.json +1 -1
- browsergym/workarena/data_files/task_configs/dashboard_retrieval_minmax_task.json +1 -1
- browsergym/workarena/data_files/task_configs/dashboard_retrieval_value_task.json +1 -1
- browsergym/workarena/data_files/task_configs/filter_service_catalog_item_list_task.json +1 -1
- browsergym/workarena/data_files/task_configs/impersonation_users.json +1 -1
- browsergym/workarena/data_files/task_configs/report_retrieval_minmax_task.json +1 -1
- browsergym/workarena/data_files/task_configs/report_retrieval_value_task.json +1 -1
- browsergym/workarena/human_eval/console.js +176 -0
- browsergym/workarena/human_eval/tool.py +366 -0
- browsergym/workarena/install.py +81 -20
- browsergym/workarena/tasks/base.py +55 -20
- browsergym/workarena/tasks/comp_building_block.py +4 -0
- browsergym/workarena/tasks/compositional/__init__.py +76 -0
- browsergym/workarena/tasks/compositional/base.py +364 -0
- browsergym/workarena/tasks/compositional/dash_do_base.py +1366 -0
- browsergym/workarena/tasks/compositional/dash_do_catalog.py +1127 -0
- browsergym/workarena/tasks/compositional/dash_do_catalog_infeasible.py +2047 -0
- browsergym/workarena/tasks/compositional/dash_do_create_incident.py +403 -0
- browsergym/workarena/tasks/compositional/dash_do_create_incident_infeasible.py +278 -0
- browsergym/workarena/tasks/compositional/dash_do_create_problem.py +336 -0
- browsergym/workarena/tasks/compositional/dash_do_create_problem_infeasible.py +235 -0
- browsergym/workarena/tasks/compositional/dash_do_filter.py +1600 -0
- browsergym/workarena/tasks/compositional/dash_do_request_item.py +1315 -0
- browsergym/workarena/tasks/compositional/dash_do_request_item_infeasible.py +693 -0
- browsergym/workarena/tasks/compositional/delete_record.py +341 -0
- browsergym/workarena/tasks/compositional/edit_knowledge_base.py +457 -0
- browsergym/workarena/tasks/compositional/expense_management.py +598 -0
- browsergym/workarena/tasks/compositional/filter_and_do.py +139 -0
- browsergym/workarena/tasks/compositional/find_and_order_item.py +345 -0
- browsergym/workarena/tasks/compositional/manage_change_request_schedule.py +1417 -0
- browsergym/workarena/tasks/compositional/mark_duplicate_problems.py +499 -0
- browsergym/workarena/tasks/compositional/maximize_investment_return.py +1763 -0
- browsergym/workarena/tasks/compositional/navigate_and_do.py +1151 -0
- browsergym/workarena/tasks/compositional/navigate_and_do_infeasible.py +2100 -0
- browsergym/workarena/tasks/compositional/offboard_user.py +207 -0
- browsergym/workarena/tasks/compositional/onboard_user.py +226 -0
- browsergym/workarena/tasks/compositional/update_task.py +145 -0
- browsergym/workarena/tasks/compositional/utils/curriculum.py +215 -0
- browsergym/workarena/tasks/compositional/utils/infeasible_configs.py +151 -0
- browsergym/workarena/tasks/compositional/utils/knapsack.py +192 -0
- browsergym/workarena/tasks/compositional/warranty_check.py +227 -0
- browsergym/workarena/tasks/compositional/work_assignment.py +804 -0
- browsergym/workarena/tasks/compositional/workload_balancing.py +396 -0
- browsergym/workarena/tasks/dashboard.py +188 -8
- browsergym/workarena/tasks/form.py +1024 -232
- browsergym/workarena/tasks/knowledge.py +216 -25
- browsergym/workarena/tasks/list.py +519 -102
- browsergym/workarena/tasks/mark_duplicate_problem.py +171 -0
- browsergym/workarena/tasks/navigation.py +55 -13
- browsergym/workarena/tasks/scripts/extract_all_menu_items.py +9 -2
- browsergym/workarena/tasks/scripts/generate_dashboard_configs.py +6 -5
- browsergym/workarena/tasks/scripts/service_catalog.py +2 -1
- browsergym/workarena/tasks/scripts/validate.py +8 -2
- browsergym/workarena/tasks/send_chat_message.py +90 -0
- browsergym/workarena/tasks/service_catalog.py +94 -26
- browsergym/workarena/tasks/utils/form.py +1 -4
- browsergym/workarena/tasks/utils/private_tasks.py +63 -0
- browsergym/workarena/tasks/utils/utils.py +13 -0
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/METADATA +19 -18
- browsergym_workarena-0.3.0.dist-info/RECORD +138 -0
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/entry_points.txt +1 -0
- browsergym_workarena-0.2.1.dist-info/RECORD +0 -85
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/WHEEL +0 -0
- {browsergym_workarena-0.2.1.dist-info → browsergym_workarena-0.3.0.dist-info}/licenses/LICENSE +0 -0
browsergym/workarena/__init__.py
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.3.0"
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import numpy as np
|
|
2
5
|
|
|
3
6
|
from browsergym.core.registration import register_task
|
|
4
7
|
|
|
8
|
+
from .tasks.comp_building_block import CompositionalBuildingBlockTask
|
|
5
9
|
from .tasks.dashboard import __TASKS__ as DASHBOARD_TASKS
|
|
6
10
|
from .tasks.form import __TASKS__ as FORM_TASKS
|
|
7
11
|
from .tasks.knowledge import __TASKS__ as KB_TASKS
|
|
8
12
|
from .tasks.list import __TASKS__ as LIST_TASKS
|
|
9
13
|
from .tasks.navigation import __TASKS__ as NAVIGATION_TASKS
|
|
10
14
|
from .tasks.service_catalog import __TASKS__ as SERVICE_CATALOG_TASKS
|
|
15
|
+
from .tasks.compositional.base import CompositionalTask
|
|
11
16
|
|
|
12
17
|
ALL_WORKARENA_TASKS = [
|
|
13
18
|
*DASHBOARD_TASKS,
|
|
@@ -17,6 +22,13 @@ ALL_WORKARENA_TASKS = [
|
|
|
17
22
|
*NAVIGATION_TASKS,
|
|
18
23
|
*SERVICE_CATALOG_TASKS,
|
|
19
24
|
]
|
|
25
|
+
ATOMIC_TASKS = [
|
|
26
|
+
task
|
|
27
|
+
for task in ALL_WORKARENA_TASKS
|
|
28
|
+
if inspect.isclass(task)
|
|
29
|
+
and not issubclass(task, CompositionalTask)
|
|
30
|
+
and not issubclass(task, CompositionalBuildingBlockTask)
|
|
31
|
+
]
|
|
20
32
|
|
|
21
33
|
# register the WorkArena benchmark
|
|
22
34
|
for task in ALL_WORKARENA_TASKS:
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from faker import Faker
|
|
3
|
+
|
|
4
|
+
fake = Faker()
|
|
5
|
+
|
|
6
|
+
from .utils import table_api_call
|
|
7
|
+
|
|
8
|
+
from ..instance import SNowInstance
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_category(
|
|
12
|
+
instance: SNowInstance,
|
|
13
|
+
list_name: str,
|
|
14
|
+
category_name: str = None,
|
|
15
|
+
) -> list[str]:
|
|
16
|
+
"""
|
|
17
|
+
NOTE: This function creates a new category in the given list. Because categories are in a drop-down list, adding more
|
|
18
|
+
categories will make the list longer and this will affect the difficulty of the task. Use only if you are certain you know
|
|
19
|
+
what you are doing.
|
|
20
|
+
|
|
21
|
+
Create a category for a given list
|
|
22
|
+
|
|
23
|
+
Parameters:
|
|
24
|
+
-----------
|
|
25
|
+
instance: SNowInstance
|
|
26
|
+
The instance to create the category in
|
|
27
|
+
list_name: str
|
|
28
|
+
The name of the list to create the category for (e.g. problem, incident, etc.)
|
|
29
|
+
category_name: str
|
|
30
|
+
The name of the category to create, defaults to a random category name
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
--------
|
|
34
|
+
category_name, sys_id
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
warnings.warn(
|
|
38
|
+
"This function creates a new category in the given list. Because categories are in a drop-down list, adding more "
|
|
39
|
+
"categories will make the list longer and this will affect the difficulty of the task. Use only if you are certain you know "
|
|
40
|
+
"what you are doing.",
|
|
41
|
+
UserWarning,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if category_name is None:
|
|
45
|
+
category_name = fake.word() + "-" + fake.word()
|
|
46
|
+
|
|
47
|
+
# Create category
|
|
48
|
+
category_data = {
|
|
49
|
+
"name": list_name,
|
|
50
|
+
"element": "category",
|
|
51
|
+
"value": category_name,
|
|
52
|
+
}
|
|
53
|
+
result = table_api_call(
|
|
54
|
+
instance=instance,
|
|
55
|
+
table="sys_choice",
|
|
56
|
+
json=category_data,
|
|
57
|
+
method="POST",
|
|
58
|
+
wait_for_record=True,
|
|
59
|
+
)["result"]
|
|
60
|
+
|
|
61
|
+
sys_id = result["sys_id"]
|
|
62
|
+
|
|
63
|
+
return category_name, sys_id
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_categories(instance, list_name):
|
|
67
|
+
"""Get the name of the categories for a given list name"""
|
|
68
|
+
categories = table_api_call(
|
|
69
|
+
instance=instance,
|
|
70
|
+
table="sys_choice",
|
|
71
|
+
params={"sysparm_query": f"name={list_name}^element=category", "sysparm_fields": "value"},
|
|
72
|
+
)["result"]
|
|
73
|
+
|
|
74
|
+
return categories
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import faker
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
fake = faker.Faker()
|
|
5
|
+
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
|
|
8
|
+
from .category import get_categories
|
|
9
|
+
from .utils import table_api_call
|
|
10
|
+
|
|
11
|
+
from ..instance import SNowInstance
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_change_request(
|
|
15
|
+
instance: SNowInstance,
|
|
16
|
+
user_sys_id: str,
|
|
17
|
+
impact: int,
|
|
18
|
+
risk: int,
|
|
19
|
+
start_date: datetime = "",
|
|
20
|
+
end_date: datetime = "",
|
|
21
|
+
hashtag: str = "",
|
|
22
|
+
short_description: str = None,
|
|
23
|
+
random: np.random = None,
|
|
24
|
+
) -> list[str]:
|
|
25
|
+
"""
|
|
26
|
+
Create a change request
|
|
27
|
+
|
|
28
|
+
Parameters:
|
|
29
|
+
-----------
|
|
30
|
+
instance: SNowInstance
|
|
31
|
+
The instance to create the change request in
|
|
32
|
+
user_sys_id: str
|
|
33
|
+
The sys_id of the user to assign the problem to
|
|
34
|
+
impact: str
|
|
35
|
+
The impact of the change request; ranges from 1 (high) to 3 (low)
|
|
36
|
+
risk: str
|
|
37
|
+
The risk of the change request; ranges from 2 (high) to 4 (low)
|
|
38
|
+
start_date: datetime.datetime
|
|
39
|
+
The start date of the change request; empty if not set
|
|
40
|
+
end_date: datetime.datetime
|
|
41
|
+
The end date of the change request; empty if not set
|
|
42
|
+
hashtag: str
|
|
43
|
+
The name of the hashtag for the change request. If "", no hashtag will be added
|
|
44
|
+
short_description: str
|
|
45
|
+
The short description of the change request. If None, a random sentence will be generated
|
|
46
|
+
random: np.random
|
|
47
|
+
The random number generator
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
--------
|
|
51
|
+
sys_id of the change request
|
|
52
|
+
number of the change request
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
if short_description is None:
|
|
56
|
+
short_description = fake.sentence(4)
|
|
57
|
+
categories = get_categories(instance=instance, list_name="change_request")
|
|
58
|
+
category = random.choice(categories)
|
|
59
|
+
|
|
60
|
+
cfg = {
|
|
61
|
+
"reason": "broken",
|
|
62
|
+
"upon_reject": "cancel",
|
|
63
|
+
"type": "emergency",
|
|
64
|
+
"state": "-5",
|
|
65
|
+
"phase": "requested",
|
|
66
|
+
"impact": str(impact),
|
|
67
|
+
"active": "true",
|
|
68
|
+
"short_description": short_description + " " + hashtag,
|
|
69
|
+
"assigned_to": user_sys_id,
|
|
70
|
+
"start_date": str(start_date),
|
|
71
|
+
"end_date": str(end_date),
|
|
72
|
+
"upon_approval": "proceed",
|
|
73
|
+
"justification": fake.sentence(),
|
|
74
|
+
"implementation_plan": fake.sentence(),
|
|
75
|
+
"phase_state": "open",
|
|
76
|
+
"risk": str(risk),
|
|
77
|
+
"cab_required": "false",
|
|
78
|
+
"category": category,
|
|
79
|
+
}
|
|
80
|
+
result = table_api_call(
|
|
81
|
+
instance=instance,
|
|
82
|
+
table="change_request",
|
|
83
|
+
method="POST",
|
|
84
|
+
json=cfg,
|
|
85
|
+
)["result"]
|
|
86
|
+
|
|
87
|
+
return result["sys_id"], result["number"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import numpy as np
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from faker import Faker
|
|
6
|
+
|
|
7
|
+
fake = Faker()
|
|
8
|
+
|
|
9
|
+
from ..instance import SNowInstance
|
|
10
|
+
from .utils import table_api_call
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_computer_asset(
|
|
14
|
+
instance: SNowInstance,
|
|
15
|
+
asset_tag: str,
|
|
16
|
+
warranty_expiration_date: str = None,
|
|
17
|
+
user_sys_id: str = None,
|
|
18
|
+
computer_model_info: dict = None,
|
|
19
|
+
random: np.random = None,
|
|
20
|
+
):
|
|
21
|
+
"""Create a hardware asset -computer model- and assign it to a user
|
|
22
|
+
Args:
|
|
23
|
+
--------
|
|
24
|
+
instance (SNowInstance):
|
|
25
|
+
The instance to create the hardware asset in
|
|
26
|
+
asset_tag (str):
|
|
27
|
+
The asset tag of the hardware asset
|
|
28
|
+
warranty_expiration_date (str):
|
|
29
|
+
The warranty expiration date of the hardware asset. If None, a random date is chosen
|
|
30
|
+
user_sys_id (str):
|
|
31
|
+
The sys_id of the user to assign the hardware asset to. If None, the hardware asset is not assigned to any user
|
|
32
|
+
computer_model_info (dict):
|
|
33
|
+
Contains the sys_id and short_description of the computer model to create the hardware asset with.
|
|
34
|
+
If None, a random computer model is chosen
|
|
35
|
+
random (np.random):
|
|
36
|
+
The random number generator
|
|
37
|
+
Returns:
|
|
38
|
+
--------
|
|
39
|
+
sys_id (str):
|
|
40
|
+
The sys_id of the created hardware asset
|
|
41
|
+
computer_model (dict):
|
|
42
|
+
The computer model information
|
|
43
|
+
warranty_expiration_date (str):
|
|
44
|
+
The warranty expiration date of the hardware asset
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if computer_model_info is None:
|
|
48
|
+
# Get the sys_id of the 'Computer' category
|
|
49
|
+
computer_model_sys_id = table_api_call(
|
|
50
|
+
instance=instance,
|
|
51
|
+
table="cmdb_model_category",
|
|
52
|
+
# The cmdb_model_category is the sys_id for the hardware category; computer in this case
|
|
53
|
+
params={
|
|
54
|
+
"sysparm_query": f"name=Computer",
|
|
55
|
+
"sysparm_fields": "sys_id",
|
|
56
|
+
},
|
|
57
|
+
)["result"][0]["sys_id"]
|
|
58
|
+
# Randomly choose a computer model if needed
|
|
59
|
+
computer_models = table_api_call(
|
|
60
|
+
instance=instance,
|
|
61
|
+
table="cmdb_model",
|
|
62
|
+
# The cmdb_model_category is the sys_id for the hardware category;
|
|
63
|
+
params={
|
|
64
|
+
"sysparm_query": f"cmdb_model_category={computer_model_sys_id}",
|
|
65
|
+
"sysparm_fields": "sys_id,short_description",
|
|
66
|
+
},
|
|
67
|
+
)["result"]
|
|
68
|
+
computer_model = random.choice(computer_models)
|
|
69
|
+
if warranty_expiration_date is None:
|
|
70
|
+
# Warranty expiration date is randomly selected between 1 year ago and 1 year from now
|
|
71
|
+
warranty_expiration_date = str(fake.date_between(start_date="-1y", end_date="+1y"))
|
|
72
|
+
|
|
73
|
+
# Create hardware asset
|
|
74
|
+
hardware_result = table_api_call(
|
|
75
|
+
instance=instance,
|
|
76
|
+
table="alm_hardware",
|
|
77
|
+
data=json.dumps(
|
|
78
|
+
{
|
|
79
|
+
"assigned_to": user_sys_id,
|
|
80
|
+
"asset_tag": asset_tag,
|
|
81
|
+
"display_name": asset_tag + " - " + computer_model["short_description"],
|
|
82
|
+
"model": computer_model["sys_id"],
|
|
83
|
+
"model_category": computer_model_sys_id,
|
|
84
|
+
"warranty_expiration": warranty_expiration_date,
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
method="POST",
|
|
88
|
+
)["result"]
|
|
89
|
+
|
|
90
|
+
return hardware_result["sys_id"], computer_model, warranty_expiration_date
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from faker import Faker
|
|
3
|
+
|
|
4
|
+
fake = Faker()
|
|
5
|
+
|
|
6
|
+
from .utils import table_api_call
|
|
7
|
+
|
|
8
|
+
from ..instance import SNowInstance
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_cost_center_sysid(instance, cost_center_name):
|
|
12
|
+
"""Get the sys_id of a cost center by its name"""
|
|
13
|
+
sys_id = table_api_call(
|
|
14
|
+
instance=instance,
|
|
15
|
+
table="cmn_cost_center",
|
|
16
|
+
params={"sysparm_query": f"name={cost_center_name}", "sysparm_fields": "sys_id"},
|
|
17
|
+
)["result"][0]
|
|
18
|
+
|
|
19
|
+
return sys_id
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from faker import Faker
|
|
5
|
+
|
|
6
|
+
fake = Faker()
|
|
7
|
+
|
|
8
|
+
from .cost_center import get_cost_center_sysid
|
|
9
|
+
from .utils import table_api_call
|
|
10
|
+
|
|
11
|
+
from ..instance import SNowInstance
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_expense_line(
|
|
15
|
+
instance: SNowInstance,
|
|
16
|
+
amount: float,
|
|
17
|
+
number: str,
|
|
18
|
+
date: str,
|
|
19
|
+
short_description: str = None,
|
|
20
|
+
expense_hashtag: str = "",
|
|
21
|
+
task_sys_id: str = None,
|
|
22
|
+
cost_center_sys_id: str = None,
|
|
23
|
+
summary_type: str = "run_business",
|
|
24
|
+
user_sys_id: str = None,
|
|
25
|
+
):
|
|
26
|
+
"""Create a hardware asset -computer model- and assign it to a user
|
|
27
|
+
Args:
|
|
28
|
+
--------
|
|
29
|
+
instance (SNowInstance):
|
|
30
|
+
The instance to create the hardware asset in
|
|
31
|
+
amount (float):
|
|
32
|
+
The amount of the expense line
|
|
33
|
+
number (str):
|
|
34
|
+
The number of the expense line
|
|
35
|
+
date (str):
|
|
36
|
+
The date of the expense line
|
|
37
|
+
short_description (str):
|
|
38
|
+
The short description of the expense line; if None, a random one will be generated
|
|
39
|
+
expense_hashtag (str):
|
|
40
|
+
The hashtag of the expense line (added to the short description)
|
|
41
|
+
task_sys_id (str):
|
|
42
|
+
The sys id of the task to file the expense line under
|
|
43
|
+
cost_center_sys_id (str):
|
|
44
|
+
The sys id of the cost center to file the expense line under
|
|
45
|
+
summary_type (str):
|
|
46
|
+
The summary type of the expense line (choice of "run_business", "grow_business", "transform_business")
|
|
47
|
+
user_sys_id (str):
|
|
48
|
+
The sys_id of the user to assign the hardware asset to. If None, the hardware asset is not assigned to any user
|
|
49
|
+
Returns:
|
|
50
|
+
--------
|
|
51
|
+
sys_id (str):
|
|
52
|
+
The sys_id of the created expense_line
|
|
53
|
+
expense_line_number (str):
|
|
54
|
+
The number of the created expense_line
|
|
55
|
+
"""
|
|
56
|
+
if cost_center_sys_id is None:
|
|
57
|
+
# sys_id of the engineering cost center
|
|
58
|
+
cost_center_sys_id = table_api_call(
|
|
59
|
+
instance=instance,
|
|
60
|
+
table="cmn_cost_center",
|
|
61
|
+
params={"sysparm_query": "name=Engineering"},
|
|
62
|
+
)["result"][0]["sys_id"]
|
|
63
|
+
|
|
64
|
+
if short_description is None:
|
|
65
|
+
short_description = fake.sentence(4)
|
|
66
|
+
|
|
67
|
+
expense_cfg = {
|
|
68
|
+
"date": date,
|
|
69
|
+
"base_expense": "",
|
|
70
|
+
"short_description": short_description + " " + expense_hashtag,
|
|
71
|
+
"summary_type": summary_type,
|
|
72
|
+
"summary_type": "run_business",
|
|
73
|
+
"type": "one-time",
|
|
74
|
+
"number": f"{number}",
|
|
75
|
+
"task": f"{task_sys_id}",
|
|
76
|
+
"state": "processed",
|
|
77
|
+
"amount": f"{amount}",
|
|
78
|
+
"cost_center": f"{cost_center_sys_id}",
|
|
79
|
+
"user": f"{user_sys_id}",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
result = table_api_call(
|
|
83
|
+
instance=instance,
|
|
84
|
+
table="fm_expense_line",
|
|
85
|
+
json=expense_cfg,
|
|
86
|
+
method="POST",
|
|
87
|
+
)["result"]
|
|
88
|
+
|
|
89
|
+
return result["sys_id"], result["number"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from faker import Faker
|
|
2
|
+
from ..instance import SNowInstance
|
|
3
|
+
|
|
4
|
+
fake = Faker()
|
|
5
|
+
|
|
6
|
+
from .utils import table_api_call
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def create_incident(
|
|
10
|
+
instance: SNowInstance,
|
|
11
|
+
incident_number: int,
|
|
12
|
+
caller_sys_id: str,
|
|
13
|
+
category: str,
|
|
14
|
+
impact: int,
|
|
15
|
+
urgency: int,
|
|
16
|
+
priority: int,
|
|
17
|
+
incident_hastag: str = None,
|
|
18
|
+
assigned_to: str = None,
|
|
19
|
+
):
|
|
20
|
+
incident_config = {
|
|
21
|
+
"task_effective_number": incident_number,
|
|
22
|
+
"number": incident_number,
|
|
23
|
+
"state": 2,
|
|
24
|
+
"knowledge": False,
|
|
25
|
+
"impact": impact,
|
|
26
|
+
"active": True,
|
|
27
|
+
"priority": priority,
|
|
28
|
+
"caller_id": caller_sys_id,
|
|
29
|
+
"short_description": incident_hastag if incident_hastag else " ".join(fake.words(5)),
|
|
30
|
+
"description": " ".join(fake.words(10)),
|
|
31
|
+
"incident_state": 2,
|
|
32
|
+
"urgency": urgency,
|
|
33
|
+
"severity": 3,
|
|
34
|
+
"category": category,
|
|
35
|
+
}
|
|
36
|
+
if assigned_to:
|
|
37
|
+
incident_config["assigned_to"] = assigned_to
|
|
38
|
+
|
|
39
|
+
incident_response = table_api_call(
|
|
40
|
+
instance=instance,
|
|
41
|
+
table="incident",
|
|
42
|
+
json=incident_config,
|
|
43
|
+
method="POST",
|
|
44
|
+
)["result"]
|
|
45
|
+
return incident_response
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from ..instance import SNowInstance
|
|
2
|
+
from .utils import table_api_call
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def give_kb_read_permissions(admin_instance, user_sys_id, user_name, kb_sys_id, kb_name):
|
|
6
|
+
# Need admin permissions to give KB permissions to the user
|
|
7
|
+
|
|
8
|
+
# Create user criteria
|
|
9
|
+
user_criteria_data = {
|
|
10
|
+
"user": user_sys_id,
|
|
11
|
+
"name": f"{user_name} read KB",
|
|
12
|
+
"short_description": f"Let {user_name} read {kb_name}",
|
|
13
|
+
}
|
|
14
|
+
criteria_response = table_api_call(
|
|
15
|
+
instance=admin_instance, table="user_criteria", json=user_criteria_data, method="POST"
|
|
16
|
+
)["result"]
|
|
17
|
+
criteria_sys_id = criteria_response["sys_id"]
|
|
18
|
+
|
|
19
|
+
# Add user criteria entry to allow users to access the ADHOC KB
|
|
20
|
+
kb_uc_can_read_mtom_data = {
|
|
21
|
+
"user_criteria": criteria_sys_id,
|
|
22
|
+
"kb_knowledge_base": kb_sys_id,
|
|
23
|
+
}
|
|
24
|
+
_ = table_api_call(
|
|
25
|
+
instance=admin_instance,
|
|
26
|
+
table="kb_uc_can_read_mtom",
|
|
27
|
+
json=kb_uc_can_read_mtom_data,
|
|
28
|
+
method="POST",
|
|
29
|
+
)["result"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import faker
|
|
2
|
+
|
|
3
|
+
fake = faker.Faker()
|
|
4
|
+
|
|
5
|
+
from ..instance import SNowInstance
|
|
6
|
+
from .utils import table_api_call
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def create_problem(
|
|
10
|
+
instance: SNowInstance,
|
|
11
|
+
priority: str,
|
|
12
|
+
user_sys_id: str,
|
|
13
|
+
problem_hashtag: str,
|
|
14
|
+
short_description: str = None,
|
|
15
|
+
return_number: bool = False,
|
|
16
|
+
) -> list[str]:
|
|
17
|
+
"""
|
|
18
|
+
Create a problem with a random cause, description, and short description. The problem is assigned to a user and
|
|
19
|
+
is created with a hashtag.
|
|
20
|
+
|
|
21
|
+
Parameters:
|
|
22
|
+
-----------
|
|
23
|
+
instance: SNowInstance
|
|
24
|
+
The instance to create the problem in
|
|
25
|
+
priority: str
|
|
26
|
+
The priority of the problem
|
|
27
|
+
user_sys_id: str
|
|
28
|
+
The sys_id of the user to assign the problem to
|
|
29
|
+
problem_hashtag: str
|
|
30
|
+
The name of the hashtag for the problem
|
|
31
|
+
short_description: str
|
|
32
|
+
The short description of the problem (optional). if not provided, a random one will be generated
|
|
33
|
+
return_number: bool
|
|
34
|
+
whether or not to return the problem number that was created
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
--------
|
|
38
|
+
sys_id of the problem
|
|
39
|
+
problem_number (optional)
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
cause = fake.sentence()
|
|
43
|
+
description = fake.text()
|
|
44
|
+
if short_description is None:
|
|
45
|
+
short_description = fake.sentence(4)
|
|
46
|
+
|
|
47
|
+
# Priority is a read-only field defined by a combo of impact and urgency. The mapping is as follows:
|
|
48
|
+
# https://docs.servicenow.com/bundle/washingtondc-it-service-management/page/product/problem-management/concept/prioritise-problems.html
|
|
49
|
+
priority_to_impact_and_urgency = {
|
|
50
|
+
1: (1, 1),
|
|
51
|
+
2: (1, 2),
|
|
52
|
+
3: (1, 3),
|
|
53
|
+
4: (2, 3),
|
|
54
|
+
5: (3, 3),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impact, urgency = priority_to_impact_and_urgency[priority]
|
|
58
|
+
|
|
59
|
+
problem_cfg = {
|
|
60
|
+
"made_sla": True,
|
|
61
|
+
"upon_reject": "cancel",
|
|
62
|
+
"cause_notes": f" <p>{cause}</p> ",
|
|
63
|
+
"fix_notes": " placeholder ", # placeholder value - will not work without a fix note
|
|
64
|
+
"knowledge": False,
|
|
65
|
+
"major_problem": False,
|
|
66
|
+
"impact": f"{impact}",
|
|
67
|
+
"active": False,
|
|
68
|
+
"sys_domain_path": "/",
|
|
69
|
+
"short_description": f"{short_description} {problem_hashtag}",
|
|
70
|
+
"known_error": False,
|
|
71
|
+
"description": f"{description}",
|
|
72
|
+
"sla_due": "2021-04-11 17:39:07",
|
|
73
|
+
"closed_at": "",
|
|
74
|
+
"resolution_code": "fix_applied",
|
|
75
|
+
"urgency": f"{urgency}",
|
|
76
|
+
"assigned_to": f"{user_sys_id}",
|
|
77
|
+
"active": True,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
result = table_api_call(
|
|
81
|
+
instance=instance,
|
|
82
|
+
table="problem",
|
|
83
|
+
json=problem_cfg,
|
|
84
|
+
method="POST",
|
|
85
|
+
)["result"]
|
|
86
|
+
|
|
87
|
+
if return_number:
|
|
88
|
+
return result["sys_id"], result["number"]
|
|
89
|
+
|
|
90
|
+
return result["sys_id"]
|