browsergym-workarena 0.1.0rc6__py3-none-any.whl → 0.2.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.
Files changed (43) hide show
  1. browsergym/workarena/__init__.py +7 -2
  2. browsergym/workarena/api/ui_themes.py +35 -0
  3. browsergym/workarena/api/user.py +153 -0
  4. browsergym/workarena/api/utils.py +1 -1
  5. browsergym/workarena/config.py +43 -1
  6. browsergym/workarena/data_files/setup_files/lists/expected_asset_list_columns.json +34 -1
  7. browsergym/workarena/data_files/setup_files/lists/expected_change_request_list_columns.json +48 -1
  8. browsergym/workarena/data_files/setup_files/lists/expected_hardware_list_columns.json +53 -1
  9. browsergym/workarena/data_files/setup_files/lists/expected_incident_list_columns.json +28 -1
  10. browsergym/workarena/data_files/setup_files/lists/expected_service_catalog_list_columns.json +29 -1
  11. browsergym/workarena/data_files/setup_files/ui_themes/workarena_themes.xml +2313 -0
  12. browsergym/workarena/data_files/task_configs/dashboard_retrieval_minmax_task.json +1 -0
  13. browsergym/workarena/data_files/task_configs/dashboard_retrieval_value_task.json +1 -0
  14. browsergym/workarena/data_files/task_configs/report_retrieval_minmax_task.json +1 -0
  15. browsergym/workarena/data_files/task_configs/report_retrieval_value_task.json +1 -0
  16. browsergym/workarena/data_files/task_configs/sort_asset_list_task.json +547 -11391
  17. browsergym/workarena/data_files/task_configs/sort_change_request_list_task.json +558 -11090
  18. browsergym/workarena/data_files/task_configs/sort_hardware_list_task.json +576 -11162
  19. browsergym/workarena/data_files/task_configs/sort_incident_list_task.json +528 -11172
  20. browsergym/workarena/data_files/task_configs/sort_service_catalog_item_list_task.json +533 -11491
  21. browsergym/workarena/data_files/task_configs/sort_user_list_task.json +568 -10582
  22. browsergym/workarena/install.py +625 -153
  23. browsergym/workarena/tasks/base.py +85 -26
  24. browsergym/workarena/tasks/dashboard.py +620 -0
  25. browsergym/workarena/tasks/form.py +127 -90
  26. browsergym/workarena/tasks/knowledge.py +30 -14
  27. browsergym/workarena/tasks/list.py +157 -65
  28. browsergym/workarena/tasks/navigation.py +18 -16
  29. browsergym/workarena/tasks/scripts/generate_dashboard_configs.py +272 -0
  30. browsergym/workarena/tasks/scripts/generate_forms.py +2 -2
  31. browsergym/workarena/tasks/scripts/list.py +33 -9
  32. browsergym/workarena/tasks/scripts/validate.py +2 -2
  33. browsergym/workarena/tasks/service_catalog.py +106 -74
  34. browsergym/workarena/tasks/utils/form.py +5 -3
  35. browsergym/workarena/tasks/utils/js_utils.js +123 -2
  36. browsergym/workarena/tasks/utils/string.py +15 -0
  37. browsergym/workarena/tasks/utils/utils.py +20 -0
  38. browsergym/workarena/utils.py +31 -2
  39. {browsergym_workarena-0.1.0rc6.dist-info → browsergym_workarena-0.2.0.dist-info}/METADATA +7 -3
  40. {browsergym_workarena-0.1.0rc6.dist-info → browsergym_workarena-0.2.0.dist-info}/RECORD +43 -32
  41. {browsergym_workarena-0.1.0rc6.dist-info → browsergym_workarena-0.2.0.dist-info}/WHEEL +1 -1
  42. {browsergym_workarena-0.1.0rc6.dist-info → browsergym_workarena-0.2.0.dist-info}/entry_points.txt +0 -0
  43. {browsergym_workarena-0.1.0rc6.dist-info → browsergym_workarena-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,8 @@
1
- __version__ = "0.1.0rc6"
1
+ __version__ = "0.2.0"
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,
@@ -18,4 +20,7 @@ ALL_WORKARENA_TASKS = [
18
20
 
19
21
  # register the WorkArena benchmark
20
22
  for task in ALL_WORKARENA_TASKS:
21
- register_task(task.get_task_id(), task)
23
+ register_task(
24
+ task.get_task_id(),
25
+ task,
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" in info:
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)
@@ -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 = ["utah"]
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"
@@ -1 +1,34 @@
1
- ["model.display_name", "model_category", "asset_tag", "substatus", "asset_function", "install_status", "sys_class_name", "assigned_to", "serial_number", "location"]
1
+ [
2
+ "model.display_name",
3
+ "model_category",
4
+ "asset_tag",
5
+ "substatus",
6
+ "asset_function",
7
+ "install_status",
8
+ "sys_class_name",
9
+ "assigned_to",
10
+ "serial_number",
11
+ "location",
12
+ "vendor",
13
+ "assigned",
14
+ "company",
15
+ "cost",
16
+ "department",
17
+ "display_name",
18
+ "model",
19
+ "sys_updated_by",
20
+ "sys_updated_on",
21
+ "sys_mod_count",
22
+ "retirement_date",
23
+ "retired",
24
+ "residual",
25
+ "quantity",
26
+ "purchase_date",
27
+ "po_number",
28
+ "order_date",
29
+ "delivery_date",
30
+ "managed_by",
31
+ "invoice_number",
32
+ "cost_center",
33
+ "warranty_expiration"
34
+ ]
@@ -1 +1,48 @@
1
- ["requested_by", "type", "sys_created_on", "end_date", "assignment_group", "assigned_to", "risk", "start_date", "state", "number", "short_description", "chg_model"]
1
+ [
2
+ "number",
3
+ "short_description",
4
+ "chg_model",
5
+ "type",
6
+ "state",
7
+ "risk",
8
+ "start_date",
9
+ "end_date",
10
+ "requested_by",
11
+ "assignment_group",
12
+ "assigned_to",
13
+ "sys_created_on",
14
+ "active",
15
+ "work_end",
16
+ "work_start",
17
+ "approval",
18
+ "approval_history",
19
+ "approval_set",
20
+ "backout_plan",
21
+ "category",
22
+ "change_plan",
23
+ "close_code",
24
+ "close_notes",
25
+ "closed_at",
26
+ "closed_by",
27
+ "cmdb_ci",
28
+ "conflict_last_run",
29
+ "conflict_status",
30
+ "contact_type",
31
+ "sys_created_by",
32
+ "delivery_plan",
33
+ "delivery_task",
34
+ "description",
35
+ "task_effective_number",
36
+ "escalation",
37
+ "impact",
38
+ "implementation_plan",
39
+ "justification",
40
+ "knowledge",
41
+ "made_sla",
42
+ "opened_at",
43
+ "opened_by",
44
+ "phase",
45
+ "phase_state",
46
+ "priority",
47
+ "reassignment_count"
48
+ ]
@@ -1 +1,53 @@
1
- ["display_name", "model_category", "asset_tag", "serial_number", "assigned_to", "company", "install_status", "substatus", "cost", "ci", "asset_function"]
1
+ [
2
+ "display_name",
3
+ "model_category",
4
+ "asset_tag",
5
+ "serial_number",
6
+ "assigned_to",
7
+ "company",
8
+ "install_status",
9
+ "substatus",
10
+ "cost",
11
+ "ci",
12
+ "asset_function",
13
+ "acquisition_method",
14
+ "active_to",
15
+ "assigned",
16
+ "beneficiary",
17
+ "checked_in",
18
+ "checked_out",
19
+ "sys_class_name",
20
+ "comments",
21
+ "cost_center",
22
+ "sys_created_on",
23
+ "sys_created_by",
24
+ "department",
25
+ "due",
26
+ "due_in",
27
+ "eligible_for_refresh",
28
+ "expenditure_type",
29
+ "install_date",
30
+ "invoice_number",
31
+ "justification",
32
+ "lease_id",
33
+ "location",
34
+ "managed_by",
35
+ "model",
36
+ "delivery_date",
37
+ "order_date",
38
+ "owned_by",
39
+ "po_number",
40
+ "purchase_date",
41
+ "quantity",
42
+ "request_line",
43
+ "retirement_date",
44
+ "skip_sync",
45
+ "stockroom",
46
+ "support_group",
47
+ "supported_by",
48
+ "sys_updated_on",
49
+ "sys_updated_by",
50
+ "sys_mod_count",
51
+ "vendor",
52
+ "warranty_expiration"
53
+ ]
@@ -1 +1,28 @@
1
- ["number", "caller_id", "category", "cmdb_ci", "priority", "state", "short_description", "assignment_group", "assigned_to", "child_incidents"]
1
+ [
2
+ "number",
3
+ "caller_id",
4
+ "category",
5
+ "cmdb_ci",
6
+ "priority",
7
+ "state",
8
+ "short_description",
9
+ "assignment_group",
10
+ "assigned_to",
11
+ "child_incidents",
12
+ "active",
13
+ "activity_due",
14
+ "business_duration",
15
+ "business_stc",
16
+ "closed_at",
17
+ "closed_by",
18
+ "company",
19
+ "sys_created_on",
20
+ "sys_created_on",
21
+ "description",
22
+ "calendar_duration",
23
+ "task_effective_number",
24
+ "escalation",
25
+ "impact",
26
+ "incident_state",
27
+ "location"
28
+ ]
@@ -1 +1,29 @@
1
- ["name", "short_description", "active", "roles", "sc_catalogs", "category", "price", "type", "sys_updated_on"]
1
+ [
2
+ "name",
3
+ "short_description",
4
+ "active",
5
+ "roles",
6
+ "sc_catalogs",
7
+ "category",
8
+ "price",
9
+ "type",
10
+ "sys_updated_on",
11
+ "access_type",
12
+ "sys_scope",
13
+ "availability",
14
+ "sys_class_name",
15
+ "mobile_picture_type",
16
+ "sys_created_on",
17
+ "sys_created_by",
18
+ "delivery_time",
19
+ "sys_name",
20
+ "description",
21
+ "delivery_plan",
22
+ "fulfillment_automation_level",
23
+ "flow_designer_flow",
24
+ "no_cart_v2",
25
+ "no_wishlist_v2",
26
+ "no_attachment_v2",
27
+ "hide_sp",
28
+ "no_quantity_v2"
29
+ ]