browsergym-workarena 0.1.0rc7__py3-none-any.whl → 0.2.1__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 +3 -2
- browsergym/workarena/api/ui_themes.py +35 -0
- browsergym/workarena/api/user.py +153 -0
- browsergym/workarena/api/utils.py +1 -1
- browsergym/workarena/config.py +43 -1
- browsergym/workarena/data_files/setup_files/ui_themes/workarena_themes.xml +2313 -0
- browsergym/workarena/data_files/task_configs/all_menu.json +94 -94
- browsergym/workarena/data_files/task_configs/dashboard_retrieval_minmax_task.json +1 -0
- browsergym/workarena/data_files/task_configs/dashboard_retrieval_value_task.json +1 -0
- browsergym/workarena/data_files/task_configs/filter_service_catalog_item_list_task.json +7985 -7981
- browsergym/workarena/data_files/task_configs/impersonation_users.json +2 -2
- browsergym/workarena/data_files/task_configs/report_retrieval_minmax_task.json +1 -0
- browsergym/workarena/data_files/task_configs/report_retrieval_value_task.json +1 -0
- browsergym/workarena/install.py +620 -155
- browsergym/workarena/tasks/base.py +85 -26
- browsergym/workarena/tasks/dashboard.py +620 -0
- browsergym/workarena/tasks/form.py +121 -85
- browsergym/workarena/tasks/knowledge.py +30 -14
- browsergym/workarena/tasks/list.py +121 -67
- browsergym/workarena/tasks/navigation.py +18 -16
- browsergym/workarena/tasks/scripts/generate_dashboard_configs.py +272 -0
- browsergym/workarena/tasks/scripts/generate_forms.py +2 -2
- browsergym/workarena/tasks/scripts/list.py +2 -2
- browsergym/workarena/tasks/scripts/validate.py +2 -2
- browsergym/workarena/tasks/service_catalog.py +106 -74
- browsergym/workarena/tasks/utils/form.py +5 -3
- browsergym/workarena/tasks/utils/js_utils.js +123 -2
- browsergym/workarena/tasks/utils/string.py +15 -0
- browsergym/workarena/tasks/utils/utils.py +20 -0
- browsergym/workarena/utils.py +31 -2
- {browsergym_workarena-0.1.0rc7.dist-info → browsergym_workarena-0.2.1.dist-info}/METADATA +15 -5
- {browsergym_workarena-0.1.0rc7.dist-info → browsergym_workarena-0.2.1.dist-info}/RECORD +35 -24
- {browsergym_workarena-0.1.0rc7.dist-info → browsergym_workarena-0.2.1.dist-info}/WHEEL +1 -1
- {browsergym_workarena-0.1.0rc7.dist-info → browsergym_workarena-0.2.1.dist-info}/entry_points.txt +0 -0
- {browsergym_workarena-0.1.0rc7.dist-info → browsergym_workarena-0.2.1.dist-info}/licenses/LICENSE +0 -0
browsergym/workarena/__init__.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
__version__ = "0.1
|
|
1
|
+
__version__ = "0.2.1"
|
|
2
2
|
|
|
3
3
|
from browsergym.core.registration import register_task
|
|
4
4
|
|
|
5
|
+
from .tasks.dashboard import __TASKS__ as DASHBOARD_TASKS
|
|
5
6
|
from .tasks.form import __TASKS__ as FORM_TASKS
|
|
6
7
|
from .tasks.knowledge import __TASKS__ as KB_TASKS
|
|
7
8
|
from .tasks.list import __TASKS__ as LIST_TASKS
|
|
@@ -9,6 +10,7 @@ from .tasks.navigation import __TASKS__ as NAVIGATION_TASKS
|
|
|
9
10
|
from .tasks.service_catalog import __TASKS__ as SERVICE_CATALOG_TASKS
|
|
10
11
|
|
|
11
12
|
ALL_WORKARENA_TASKS = [
|
|
13
|
+
*DASHBOARD_TASKS,
|
|
12
14
|
*FORM_TASKS,
|
|
13
15
|
*KB_TASKS,
|
|
14
16
|
*LIST_TASKS,
|
|
@@ -21,5 +23,4 @@ for task in ALL_WORKARENA_TASKS:
|
|
|
21
23
|
register_task(
|
|
22
24
|
task.get_task_id(),
|
|
23
25
|
task,
|
|
24
|
-
kwargs={"viewport": {"width": 1280, "height": 720}, "timeout": 10000},
|
|
25
26
|
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for UI themes
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .utils import table_api_call
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_workarena_theme_variants(instance):
|
|
10
|
+
"""
|
|
11
|
+
Get the list of available WorkArena UI themes
|
|
12
|
+
|
|
13
|
+
Parameters:
|
|
14
|
+
-----------
|
|
15
|
+
instance: SNowInstance
|
|
16
|
+
The ServiceNow instance to get the UI themes from
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
--------
|
|
20
|
+
list[dict]
|
|
21
|
+
The list of available WorkArena UI themes and their information
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
themes = table_api_call(
|
|
25
|
+
instance=instance,
|
|
26
|
+
table="m2m_theme_style",
|
|
27
|
+
params={
|
|
28
|
+
"sysparm_query": "style.type=variant",
|
|
29
|
+
"sysparm_fields": "theme.name,theme.sys_id,style.name,style.sys_id",
|
|
30
|
+
"sysparm_display_value": True,
|
|
31
|
+
},
|
|
32
|
+
method="GET",
|
|
33
|
+
)["result"]
|
|
34
|
+
themes = [t for t in themes if t["theme.name"] == "WorkArena"]
|
|
35
|
+
return themes
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from faker import Faker
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
fake = Faker()
|
|
6
|
+
|
|
7
|
+
from ..instance import SNowInstance
|
|
8
|
+
from .ui_themes import get_workarena_theme_variants
|
|
9
|
+
from .utils import table_api_call
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_user(
|
|
13
|
+
instance: SNowInstance,
|
|
14
|
+
first_name: str = None,
|
|
15
|
+
last_name: str = None,
|
|
16
|
+
user_name: str = None,
|
|
17
|
+
admin=True,
|
|
18
|
+
) -> list[str]:
|
|
19
|
+
"""
|
|
20
|
+
Create a user with a random username and password with an admin role
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
-----------
|
|
24
|
+
first_name: str
|
|
25
|
+
The first name of the user, defaults to a random first name
|
|
26
|
+
last_name: str
|
|
27
|
+
The last name of the user, defaults to a random last name
|
|
28
|
+
user_name: str
|
|
29
|
+
The user name of the user, defaults to first_name.last_name
|
|
30
|
+
admin: bool
|
|
31
|
+
Whether to give the user admin permissions
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
--------
|
|
35
|
+
username, password, sys_id
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
user_idx = str(random.randint(1000, 9999))
|
|
39
|
+
user_password = "aStrongPassword!"
|
|
40
|
+
first_name = fake.first_name() if not first_name else first_name
|
|
41
|
+
last_name = fake.last_name() if not last_name else last_name
|
|
42
|
+
|
|
43
|
+
# Create user
|
|
44
|
+
user_data = {
|
|
45
|
+
"user_name": f"{first_name}.{last_name}.{user_idx}" if not user_name else user_name,
|
|
46
|
+
"first_name": first_name,
|
|
47
|
+
"last_name": last_name,
|
|
48
|
+
"email": f"{first_name}.{last_name}.{user_idx}@workarena.com".lower(),
|
|
49
|
+
"user_password": user_password,
|
|
50
|
+
"active": True,
|
|
51
|
+
}
|
|
52
|
+
user_params = {"sysparm_input_display_value": True}
|
|
53
|
+
user_response = table_api_call(
|
|
54
|
+
instance=instance, table="sys_user", json=user_data, params=user_params, method="POST"
|
|
55
|
+
)["result"]
|
|
56
|
+
user_name = user_response["user_name"]
|
|
57
|
+
user_sys_id = user_response["sys_id"]
|
|
58
|
+
|
|
59
|
+
# Get admin role sys_id
|
|
60
|
+
if admin:
|
|
61
|
+
role_sys_id = table_api_call(
|
|
62
|
+
instance=instance,
|
|
63
|
+
table="sys_user_role",
|
|
64
|
+
params={"sysparm_query": "name=admin", "sysparm_fields": "sys_id"},
|
|
65
|
+
method="GET",
|
|
66
|
+
)["result"][0]["sys_id"]
|
|
67
|
+
|
|
68
|
+
# Give admin permissions
|
|
69
|
+
association_data = {"user": user_sys_id, "role": role_sys_id}
|
|
70
|
+
table_api_call(
|
|
71
|
+
instance=instance, table="sys_user_has_role", json=association_data, method="POST"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Randomly pick a UI theme and set it for the user
|
|
75
|
+
themes = get_workarena_theme_variants(instance)
|
|
76
|
+
theme = random.choice(themes)
|
|
77
|
+
set_user_preference(
|
|
78
|
+
instance, "glide.ui.polaris.theme.variant", theme["style.sys_id"], user=user_sys_id
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return user_name, user_password, user_sys_id
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def set_user_preference(instance: SNowInstance, key: str, value: str, user=None) -> dict:
|
|
85
|
+
"""
|
|
86
|
+
Set a user preference in the ServiceNow instance
|
|
87
|
+
|
|
88
|
+
Parameters:
|
|
89
|
+
-----------
|
|
90
|
+
key: str
|
|
91
|
+
The name of the preference
|
|
92
|
+
value: str
|
|
93
|
+
The value of the preference
|
|
94
|
+
user: str
|
|
95
|
+
The sys_id of the user. If None, the preference will be set globally.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
--------
|
|
99
|
+
dict
|
|
100
|
+
The preference that was set
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
if user is None:
|
|
104
|
+
# make it global
|
|
105
|
+
user = ""
|
|
106
|
+
system = True
|
|
107
|
+
else:
|
|
108
|
+
system = False
|
|
109
|
+
|
|
110
|
+
# Try to get the preference's sys_id
|
|
111
|
+
preference = table_api_call(
|
|
112
|
+
instance=instance,
|
|
113
|
+
table="sys_user_preference",
|
|
114
|
+
params={"sysparm_query": f"name={key},user={user}", "sysparm_fields": "sys_id"},
|
|
115
|
+
)["result"]
|
|
116
|
+
|
|
117
|
+
if not preference:
|
|
118
|
+
# ... The preference key doesn't exist, create it
|
|
119
|
+
pref_sysid = ""
|
|
120
|
+
method = "POST"
|
|
121
|
+
else:
|
|
122
|
+
# ... The preference key exists, update it
|
|
123
|
+
pref_sysid = "/" + preference[0]["sys_id"]
|
|
124
|
+
method = "PUT"
|
|
125
|
+
|
|
126
|
+
property = table_api_call(
|
|
127
|
+
instance=instance,
|
|
128
|
+
table=f"sys_user_preference{pref_sysid}",
|
|
129
|
+
method=method,
|
|
130
|
+
json={
|
|
131
|
+
"name": key,
|
|
132
|
+
"value": value,
|
|
133
|
+
"user": user,
|
|
134
|
+
"system": system,
|
|
135
|
+
"description": "Updated by WorkArena",
|
|
136
|
+
},
|
|
137
|
+
)["result"]
|
|
138
|
+
|
|
139
|
+
# Verify that the property was updated
|
|
140
|
+
property["user"] = (
|
|
141
|
+
property["user"].get("value") if isinstance(property["user"], dict) else property["user"]
|
|
142
|
+
)
|
|
143
|
+
assert (
|
|
144
|
+
property["value"] == value
|
|
145
|
+
), f"Error setting system property {key}, incorrect value {property['value']}, while expecting {value}."
|
|
146
|
+
assert (
|
|
147
|
+
property["user"] == user
|
|
148
|
+
), f"Error setting system property {key}, incorrect user {property['user']}, while expecting {user}."
|
|
149
|
+
assert (
|
|
150
|
+
property["system"] == str(system).lower()
|
|
151
|
+
), f"Error setting {key}, incorrect system {property['system']}, while expecting {system}."
|
|
152
|
+
|
|
153
|
+
return property
|
|
@@ -86,7 +86,7 @@ def table_column_info(instance: SNowInstance, table: str) -> dict:
|
|
|
86
86
|
|
|
87
87
|
# Clean column value choices
|
|
88
88
|
for info in meta_info.values():
|
|
89
|
-
if "choices"
|
|
89
|
+
if info.get("choices", None):
|
|
90
90
|
info["choices"] = {c["value"]: c["label"] for c in info["choices"]}
|
|
91
91
|
|
|
92
92
|
# Query the sys_dictionnary table to find more info (e.g., is this column dependent on another)
|
browsergym/workarena/config.py
CHANGED
|
@@ -7,10 +7,25 @@ from ..workarena.tasks import utils
|
|
|
7
7
|
SNOW_DATA_LOOKBACK_MINUTES = 5
|
|
8
8
|
SNOW_BROWSER_TIMEOUT = 30000 # Milliseconds
|
|
9
9
|
SNOW_JS_UTILS_FILEPATH = str(resources.files(utils).joinpath("js_utils.js"))
|
|
10
|
-
SNOW_SUPPORTED_RELEASES = ["
|
|
10
|
+
SNOW_SUPPORTED_RELEASES = ["washingtondc"]
|
|
11
11
|
|
|
12
12
|
# Path to the Menu navigation task configuration
|
|
13
13
|
ALL_MENU_PATH = str(resources.files(data_files).joinpath("task_configs/all_menu.json"))
|
|
14
|
+
|
|
15
|
+
# Path to the dashboard/report retrieval task configurations
|
|
16
|
+
DASHBOARD_RETRIEVAL_MINMAX_CONFIG_PATH = str(
|
|
17
|
+
resources.files(data_files).joinpath("task_configs/dashboard_retrieval_minmax_task.json")
|
|
18
|
+
)
|
|
19
|
+
DASHBOARD_RETRIEVAL_VALUE_CONFIG_PATH = str(
|
|
20
|
+
resources.files(data_files).joinpath("task_configs/dashboard_retrieval_value_task.json")
|
|
21
|
+
)
|
|
22
|
+
REPORT_RETRIEVAL_MINMAX_CONFIG_PATH = str(
|
|
23
|
+
resources.files(data_files).joinpath("task_configs/report_retrieval_minmax_task.json")
|
|
24
|
+
)
|
|
25
|
+
REPORT_RETRIEVAL_VALUE_CONFIG_PATH = str(
|
|
26
|
+
resources.files(data_files).joinpath("task_configs/report_retrieval_value_task.json")
|
|
27
|
+
)
|
|
28
|
+
|
|
14
29
|
# Path to knowledge base task configurations
|
|
15
30
|
KB_CONFIG_PATH = str(
|
|
16
31
|
resources.files(data_files).joinpath("task_configs/knowledge_base_configs.json")
|
|
@@ -119,6 +134,28 @@ WORKFLOWS = {
|
|
|
119
134
|
}
|
|
120
135
|
}
|
|
121
136
|
|
|
137
|
+
|
|
138
|
+
# Custom UI Themes
|
|
139
|
+
UI_THEMES_UPDATE_SET = {
|
|
140
|
+
"name": "WorkArena UI Themes",
|
|
141
|
+
"update_set": str(
|
|
142
|
+
resources.files(data_files).joinpath("setup_files/ui_themes/workarena_themes.xml")
|
|
143
|
+
),
|
|
144
|
+
"variants": [
|
|
145
|
+
"Astranova",
|
|
146
|
+
"Charlies",
|
|
147
|
+
"Great pasta",
|
|
148
|
+
"Mighty capital",
|
|
149
|
+
"Speedy tires",
|
|
150
|
+
"Skyward",
|
|
151
|
+
"Turbobots",
|
|
152
|
+
"Ultrashoes",
|
|
153
|
+
"Vitasphere",
|
|
154
|
+
"Workarena",
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
122
159
|
# Expected columns for list tasks; used in setup
|
|
123
160
|
EXPECTED_ASSET_LIST_COLUMNS_PATH = str(
|
|
124
161
|
resources.files(data_files).joinpath("setup_files/lists/expected_asset_list_columns.json")
|
|
@@ -163,3 +200,8 @@ EXPECTED_PROBLEM_FORM_FIELDS_PATH = str(
|
|
|
163
200
|
EXPECTED_USER_FORM_FIELDS_PATH = str(
|
|
164
201
|
resources.files(data_files).joinpath("setup_files/forms/expected_user_form_fields.json")
|
|
165
202
|
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# Report date filter patch flag
|
|
206
|
+
REPORT_PATCH_FLAG = "WORKARENA_DATE_FILTER_PATCH"
|
|
207
|
+
REPORT_DATE_FILTER = "2024-04-01"
|