browsergym-workarena 0.2.0__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 +95 -95
- 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 +7986 -7982
- browsergym/workarena/data_files/task_configs/impersonation_users.json +3 -3
- 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.0.dist-info → browsergym_workarena-0.3.0.dist-info}/METADATA +27 -20
- browsergym_workarena-0.3.0.dist-info/RECORD +138 -0
- {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/entry_points.txt +1 -0
- browsergym_workarena-0.2.0.dist-info/RECORD +0 -85
- {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/WHEEL +0 -0
- {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from playwright.sync_api._generated import Page
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
|
|
5
|
+
from .dash_do_base import DashboardRetrieveIncidentAndDoTask, DashDoFinalTask
|
|
6
|
+
|
|
7
|
+
from ..base import AbstractServiceNowTask
|
|
8
|
+
from ..dashboard import ReportMinMaxRetrievalTask, ReportMeanMedianModeRetrievalTask
|
|
9
|
+
|
|
10
|
+
from ...api.utils import table_api_call, db_delete_from_table
|
|
11
|
+
from ...instance import SNowInstance
|
|
12
|
+
|
|
13
|
+
from browsergym.workarena.tasks.navigation import AllMenuTask
|
|
14
|
+
from browsergym.workarena.tasks.form import CreateIncidentTask
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DashboardRetrieveIncidentAndCreateIncidentTask(DashboardRetrieveIncidentAndDoTask):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
instance: SNowInstance = None,
|
|
21
|
+
seed: int = None,
|
|
22
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
23
|
+
level: int = 2,
|
|
24
|
+
question: str = None,
|
|
25
|
+
dashboard_class: AbstractServiceNowTask = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
29
|
+
Attributes:
|
|
30
|
+
-----------
|
|
31
|
+
task_description: str
|
|
32
|
+
The start of the task description to be completed.
|
|
33
|
+
short_description: str
|
|
34
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
35
|
+
"""
|
|
36
|
+
self.filter_than = "lesser"
|
|
37
|
+
self.attribute_name = "assigned_to"
|
|
38
|
+
super().__init__(
|
|
39
|
+
instance=instance,
|
|
40
|
+
seed=seed,
|
|
41
|
+
fixed_config=fixed_config,
|
|
42
|
+
level=level,
|
|
43
|
+
question=question,
|
|
44
|
+
dashboard_class=dashboard_class,
|
|
45
|
+
)
|
|
46
|
+
self.prefix = "DCI"
|
|
47
|
+
|
|
48
|
+
def set_compositional_task(self) -> None:
|
|
49
|
+
# The unique name for the user is created once the task is instantiated
|
|
50
|
+
base_user = table_api_call(
|
|
51
|
+
instance=self.instance,
|
|
52
|
+
table="sys_user",
|
|
53
|
+
params={
|
|
54
|
+
"sysparm_query": f"sys_id={self._base_user_sysid}",
|
|
55
|
+
},
|
|
56
|
+
)["result"][0]
|
|
57
|
+
self.user_name = base_user["first_name"] + " " + base_user["last_name"]
|
|
58
|
+
|
|
59
|
+
agent_full_names, agent_value_sysids = self.get_agent_values(
|
|
60
|
+
self.attribute_name, self.filter_than
|
|
61
|
+
)
|
|
62
|
+
self.agent_value_sysids = agent_value_sysids
|
|
63
|
+
incident_numbers = []
|
|
64
|
+
for _ in range(len(agent_full_names)):
|
|
65
|
+
incident_number = "INC" + str(random.randint(1000000, 9999999))
|
|
66
|
+
while (
|
|
67
|
+
incident_number in self.all_incident_numbers or incident_number in incident_numbers
|
|
68
|
+
):
|
|
69
|
+
incident_number = "INC" + str(random.randint(1000000, 9999999))
|
|
70
|
+
incident_numbers.append(incident_number)
|
|
71
|
+
|
|
72
|
+
self.incident_numbers = incident_numbers
|
|
73
|
+
|
|
74
|
+
create_incident_subtasks = []
|
|
75
|
+
|
|
76
|
+
for agent_full_name, incident_number in zip(agent_full_names, incident_numbers):
|
|
77
|
+
self.incident_short_description = "Compulsory training for employee in probation"
|
|
78
|
+
incident_config = {
|
|
79
|
+
"fields": {
|
|
80
|
+
"caller_id": "Caller",
|
|
81
|
+
"category": "Category",
|
|
82
|
+
"short_description": "Short description",
|
|
83
|
+
"impact": "Impact",
|
|
84
|
+
"number": "Number",
|
|
85
|
+
"urgency": "Urgency",
|
|
86
|
+
"assigned_to": "Assigned to",
|
|
87
|
+
},
|
|
88
|
+
"task_fields": [
|
|
89
|
+
"caller_id",
|
|
90
|
+
"category",
|
|
91
|
+
"short_description",
|
|
92
|
+
"impact",
|
|
93
|
+
"number",
|
|
94
|
+
"urgency",
|
|
95
|
+
"assigned_to",
|
|
96
|
+
],
|
|
97
|
+
"template_record": {
|
|
98
|
+
"caller_id": self.user_name,
|
|
99
|
+
"category": "Inquiry / Help",
|
|
100
|
+
"short_description": self.incident_short_description,
|
|
101
|
+
"impact": "1 - High",
|
|
102
|
+
"number": incident_number,
|
|
103
|
+
"urgency": "1 - High",
|
|
104
|
+
"assigned_to": agent_full_name,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
create_incident_subtask = [
|
|
109
|
+
# Navigate to the incident list
|
|
110
|
+
AllMenuTask(
|
|
111
|
+
instance=self.instance,
|
|
112
|
+
fixed_config={
|
|
113
|
+
"application": "Service Desk",
|
|
114
|
+
"module": "Incidents",
|
|
115
|
+
"url": "/now/nav/ui/classic/params/target/incident_list.do",
|
|
116
|
+
},
|
|
117
|
+
is_validated=False,
|
|
118
|
+
used_in_level_2=True,
|
|
119
|
+
),
|
|
120
|
+
# Create an incident
|
|
121
|
+
CreateIncidentTask(
|
|
122
|
+
instance=self.instance,
|
|
123
|
+
fixed_config=incident_config,
|
|
124
|
+
is_validated=False,
|
|
125
|
+
used_in_level_2=True,
|
|
126
|
+
check_record_created=False,
|
|
127
|
+
),
|
|
128
|
+
]
|
|
129
|
+
create_incident_subtasks += create_incident_subtask
|
|
130
|
+
|
|
131
|
+
self.compositional_task = create_incident_subtasks
|
|
132
|
+
|
|
133
|
+
def setup_goal(self, page: Page) -> tuple[str, dict]:
|
|
134
|
+
self.create_report()
|
|
135
|
+
self.set_compositional_task()
|
|
136
|
+
config = self.fixed_config if self.fixed_config else self._get_config()
|
|
137
|
+
incident_numbers_string = ", ".join(self.incident_numbers)
|
|
138
|
+
|
|
139
|
+
if self.level == 3:
|
|
140
|
+
self.task_description = (
|
|
141
|
+
self.task_description
|
|
142
|
+
+ f"\t - Please retrieve the '{self.description_mapping[self.question]}' value of the number of incidents assigned across agents. Using this value, retrieve agents that have less than or equal number of incidents assigned to them compared to this value.\n"
|
|
143
|
+
+ f"\t For example, if you were asked to find the 'mean' or 'average' for a case where there are 4 agents assigned 1,2,3,2 incidents respectively, the mean would be 2. The list of agents required for solving the following task would be all the agents that have less than or equal to 2 assigned incidents.\n\n"
|
|
144
|
+
+ f"\t - Task: For each agent that fits the above criteria, create an 'incident' with the following information (only fill these fields and the 'incident number' from below) and assign it to them using the 'Assigned to' field: \n"
|
|
145
|
+
+ f"\t\t Category: 'Inquiry / Help', Impact: '1 - High', Urgency: '1 - High', Short description: 'Compulsory training for employee in probation', Caller: '{self.user_name}'. Make sure to use an 'incident number' from the list as described below.\n"
|
|
146
|
+
+ f"\t Importantly, you should override the default incident numbers in the form and instead use one incident number from the following list for each incident you create: {incident_numbers_string}.\n\n"
|
|
147
|
+
+ f"Note that you will create as many incidents as there are agents matching the above criteria.\n"
|
|
148
|
+
+ f"\t For example, consider the above case and say you have 3 agents with less than or equal to 2 incidents assigned to them in the chart. You will be creating '3' new incidents here, one assigned to each agent. \n\n"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
goal, info = super().setup_goal(page=page, config=config)
|
|
152
|
+
|
|
153
|
+
if self.level == 2:
|
|
154
|
+
goal = (
|
|
155
|
+
self.short_description
|
|
156
|
+
+ f"\n1. Navigate to the Reports > View/Run page. \n"
|
|
157
|
+
+ f"\n2. Given the title of the report, search for it on this page. The report shows the number of 'incidents' assigned to an 'agent'.\n"
|
|
158
|
+
+ f"\n3. Find the agents with a number of incidents assigned less than or equal to the {self.description_mapping[self.question]} of the number of assigned incidents across agents. For example, if you were asked to find the 'mean' or 'average' for a case where there are 4 agents assigned 1,2,3,2 incidents respectively, the mean would be 2. The list of agents required for solving the following task would be all the agents that have less than or equal to 2 assigned incidents.\n"
|
|
159
|
+
+ f"\n4. Navigate to Service Desk > Incidents. \n"
|
|
160
|
+
+ f"\n5. You have to create new 'incidents' from this page for all the agents based on the above criteria. Only fill the following fields when creating a new incident:- Category: 'Inquiry / Help', Impact: '1 - High', Urgency: '1 - High', Short description: 'Compulsory training for employee in probation', Caller: '{self.user_name}' and 'assign' them to each agent using the 'Assigned to' field.\n\n"
|
|
161
|
+
+ f"Importantly, you should override the default incident numbers in the form and instead use one incident number from the following list for each incident you create: {incident_numbers_string}.\n"
|
|
162
|
+
+ f"Note that you will create as many incidents as there are agents matching the above criteria."
|
|
163
|
+
+ "\nFor example, consider the above case and say you have 3 agents with less than or equal to 2 incidents assigned to them in the chart. You will be creating '3' new incidents here, one assigned to each agent. \n"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return goal, info
|
|
167
|
+
|
|
168
|
+
def validate(self, page: Page, chat_messages: list[str]) -> Tuple[float, bool, str, dict]:
|
|
169
|
+
fixed_template_record = {
|
|
170
|
+
"short_description": "Compulsory training for employee in probation",
|
|
171
|
+
"impact": "1",
|
|
172
|
+
"urgency": "1",
|
|
173
|
+
}
|
|
174
|
+
for incident_number in self.incident_numbers:
|
|
175
|
+
created_incident_response = table_api_call(
|
|
176
|
+
instance=self.instance,
|
|
177
|
+
table="incident",
|
|
178
|
+
params={
|
|
179
|
+
"sysparm_query": f"number={incident_number}",
|
|
180
|
+
"sysparm_fields": "assigned_to,impact,urgency,short_description,description",
|
|
181
|
+
},
|
|
182
|
+
method="GET",
|
|
183
|
+
)["result"]
|
|
184
|
+
if len(created_incident_response) == 0:
|
|
185
|
+
return (
|
|
186
|
+
0,
|
|
187
|
+
False,
|
|
188
|
+
"",
|
|
189
|
+
{"message": f"No incident created with number {incident_number}."},
|
|
190
|
+
)
|
|
191
|
+
elif len(created_incident_response) > 1:
|
|
192
|
+
return (
|
|
193
|
+
0,
|
|
194
|
+
False,
|
|
195
|
+
"",
|
|
196
|
+
{"message": f"Multiple incidents created with number {incident_number}."},
|
|
197
|
+
)
|
|
198
|
+
created_incident_response = created_incident_response[0]
|
|
199
|
+
if created_incident_response["assigned_to"]["value"] not in self.agent_value_sysids:
|
|
200
|
+
return (
|
|
201
|
+
0,
|
|
202
|
+
False,
|
|
203
|
+
"",
|
|
204
|
+
{"message": f"Incident {incident_number} assigned to a random agent."},
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
for key, value in fixed_template_record.items():
|
|
208
|
+
if str(created_incident_response[key]).lower() != str(value).lower():
|
|
209
|
+
return (
|
|
210
|
+
0,
|
|
211
|
+
False,
|
|
212
|
+
"",
|
|
213
|
+
{
|
|
214
|
+
"message": f"Incident {incident_number} assigned incorrect value to field {key}."
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
for agent_sysid in self.agent_value_sysids:
|
|
219
|
+
created_incident_response = table_api_call(
|
|
220
|
+
instance=self.instance,
|
|
221
|
+
table="incident",
|
|
222
|
+
params={
|
|
223
|
+
"sysparm_query": f"assigned_to={agent_sysid}^short_description={self.incident_short_description}",
|
|
224
|
+
"sysparm_fields": "assigned_to",
|
|
225
|
+
},
|
|
226
|
+
method="GET",
|
|
227
|
+
)["result"]
|
|
228
|
+
if len(created_incident_response) == 0:
|
|
229
|
+
return (
|
|
230
|
+
0,
|
|
231
|
+
False,
|
|
232
|
+
"",
|
|
233
|
+
{
|
|
234
|
+
"message": f"No incident assigned to agent {self.agents[agent_sysid]['user_name']}."
|
|
235
|
+
},
|
|
236
|
+
)
|
|
237
|
+
elif len(created_incident_response) > 1:
|
|
238
|
+
return (
|
|
239
|
+
0,
|
|
240
|
+
False,
|
|
241
|
+
"",
|
|
242
|
+
{
|
|
243
|
+
"message": f"Multiple incidents assigned to agent {self.agents[agent_sysid]['user_name']}."
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
reward, done, message, info = super().validate(page, chat_messages)
|
|
247
|
+
return reward, done, message, info
|
|
248
|
+
|
|
249
|
+
def teardown(self) -> None:
|
|
250
|
+
for incident_number in self.incident_numbers:
|
|
251
|
+
created_incident_response = table_api_call(
|
|
252
|
+
instance=self.instance,
|
|
253
|
+
table="incident",
|
|
254
|
+
params={
|
|
255
|
+
"sysparm_query": f"number={incident_number}",
|
|
256
|
+
},
|
|
257
|
+
method="GET",
|
|
258
|
+
)["result"]
|
|
259
|
+
if len(created_incident_response) > 1:
|
|
260
|
+
raise Exception("Multiple incidents created")
|
|
261
|
+
if len(created_incident_response) == 1:
|
|
262
|
+
created_incident_sysid = created_incident_response[0]["sys_id"]
|
|
263
|
+
db_delete_from_table(
|
|
264
|
+
instance=self.instance,
|
|
265
|
+
table="incident",
|
|
266
|
+
sys_id=created_incident_sysid,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return super().teardown()
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class DashboardRetrieveIncidentAndMinCreateIncidentTask(
|
|
273
|
+
DashboardRetrieveIncidentAndCreateIncidentTask, DashDoFinalTask
|
|
274
|
+
):
|
|
275
|
+
def __init__(
|
|
276
|
+
self,
|
|
277
|
+
instance: SNowInstance = None,
|
|
278
|
+
seed: int = None,
|
|
279
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
280
|
+
level: int = 2,
|
|
281
|
+
) -> None:
|
|
282
|
+
"""
|
|
283
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
284
|
+
Attributes:
|
|
285
|
+
-----------
|
|
286
|
+
task_description: str
|
|
287
|
+
The start of the task description to be completed.
|
|
288
|
+
short_description: str
|
|
289
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
290
|
+
"""
|
|
291
|
+
super().__init__(
|
|
292
|
+
instance=instance,
|
|
293
|
+
seed=seed,
|
|
294
|
+
fixed_config=fixed_config,
|
|
295
|
+
level=level,
|
|
296
|
+
question="min",
|
|
297
|
+
dashboard_class=ReportMinMaxRetrievalTask,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class DashboardRetrieveIncidentAndMeanCreateIncidentTask(
|
|
302
|
+
DashboardRetrieveIncidentAndCreateIncidentTask, DashDoFinalTask
|
|
303
|
+
):
|
|
304
|
+
def __init__(
|
|
305
|
+
self,
|
|
306
|
+
instance: SNowInstance = None,
|
|
307
|
+
seed: int = None,
|
|
308
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
309
|
+
level: int = 2,
|
|
310
|
+
) -> None:
|
|
311
|
+
"""
|
|
312
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
313
|
+
Attributes:
|
|
314
|
+
-----------
|
|
315
|
+
task_description: str
|
|
316
|
+
The start of the task description to be completed.
|
|
317
|
+
short_description: str
|
|
318
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
319
|
+
"""
|
|
320
|
+
super().__init__(
|
|
321
|
+
instance=instance,
|
|
322
|
+
seed=seed,
|
|
323
|
+
fixed_config=fixed_config,
|
|
324
|
+
level=level,
|
|
325
|
+
question="mean",
|
|
326
|
+
dashboard_class=ReportMeanMedianModeRetrievalTask,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class DashboardRetrieveIncidentAndMedianCreateIncidentTask(
|
|
331
|
+
DashboardRetrieveIncidentAndCreateIncidentTask, DashDoFinalTask
|
|
332
|
+
):
|
|
333
|
+
def __init__(
|
|
334
|
+
self,
|
|
335
|
+
instance: SNowInstance = None,
|
|
336
|
+
seed: int = None,
|
|
337
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
338
|
+
level: int = 2,
|
|
339
|
+
) -> None:
|
|
340
|
+
"""
|
|
341
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
342
|
+
Attributes:
|
|
343
|
+
-----------
|
|
344
|
+
task_description: str
|
|
345
|
+
The start of the task description to be completed.
|
|
346
|
+
short_description: str
|
|
347
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
348
|
+
"""
|
|
349
|
+
super().__init__(
|
|
350
|
+
instance=instance,
|
|
351
|
+
seed=seed,
|
|
352
|
+
fixed_config=fixed_config,
|
|
353
|
+
level=level,
|
|
354
|
+
question="median",
|
|
355
|
+
dashboard_class=ReportMeanMedianModeRetrievalTask,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class DashboardRetrieveIncidentAndModeCreateIncidentTask(
|
|
360
|
+
DashboardRetrieveIncidentAndCreateIncidentTask, DashDoFinalTask
|
|
361
|
+
):
|
|
362
|
+
def __init__(
|
|
363
|
+
self,
|
|
364
|
+
instance: SNowInstance = None,
|
|
365
|
+
seed: int = None,
|
|
366
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
367
|
+
level: int = 2,
|
|
368
|
+
) -> None:
|
|
369
|
+
"""
|
|
370
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
371
|
+
Attributes:
|
|
372
|
+
-----------
|
|
373
|
+
task_description: str
|
|
374
|
+
The start of the task description to be completed.
|
|
375
|
+
short_description: str
|
|
376
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
377
|
+
"""
|
|
378
|
+
super().__init__(
|
|
379
|
+
instance=instance,
|
|
380
|
+
seed=seed,
|
|
381
|
+
fixed_config=fixed_config,
|
|
382
|
+
level=level,
|
|
383
|
+
question="mode",
|
|
384
|
+
dashboard_class=ReportMeanMedianModeRetrievalTask,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
local_vars = locals().copy()
|
|
389
|
+
|
|
390
|
+
__TASKS__ = [
|
|
391
|
+
var
|
|
392
|
+
for var in local_vars.values()
|
|
393
|
+
if isinstance(var, type) and issubclass(var, DashDoFinalTask) and var is not DashDoFinalTask
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
DASH_AND_CREATE_INCIDENT = [
|
|
397
|
+
DashboardRetrieveIncidentAndMinCreateIncidentTask,
|
|
398
|
+
]
|
|
399
|
+
DASH_COMPUTE_AND_CREATE_INCIDENT = [
|
|
400
|
+
DashboardRetrieveIncidentAndMeanCreateIncidentTask,
|
|
401
|
+
DashboardRetrieveIncidentAndMedianCreateIncidentTask,
|
|
402
|
+
DashboardRetrieveIncidentAndModeCreateIncidentTask,
|
|
403
|
+
]
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from playwright.sync_api._generated import Page
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
|
|
5
|
+
from .dash_do_base import DashboardRetrieveIncidentAndDoInfeasibleTask, DashDoFinalTask
|
|
6
|
+
from .utils.infeasible_configs import get_infeasible_form_config
|
|
7
|
+
|
|
8
|
+
from ..base import AbstractServiceNowTask
|
|
9
|
+
from ..dashboard import ReportMinMaxRetrievalTask
|
|
10
|
+
|
|
11
|
+
from ...api.utils import table_api_call, db_delete_from_table
|
|
12
|
+
from ...instance import SNowInstance
|
|
13
|
+
|
|
14
|
+
from browsergym.workarena.tasks.navigation import AllMenuTask
|
|
15
|
+
from browsergym.workarena.tasks.form import CreateIncidentTask
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DashboardRetrieveIncidentAndCreateIncidentInfeasibleTask(
|
|
19
|
+
DashboardRetrieveIncidentAndDoInfeasibleTask
|
|
20
|
+
):
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
instance: SNowInstance = None,
|
|
24
|
+
seed: int = None,
|
|
25
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
26
|
+
level: int = 2,
|
|
27
|
+
question: str = None,
|
|
28
|
+
dashboard_class: AbstractServiceNowTask = None,
|
|
29
|
+
provide_reason: bool = True,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
33
|
+
Attributes:
|
|
34
|
+
-----------
|
|
35
|
+
task_description: str
|
|
36
|
+
The start of the task description to be completed.
|
|
37
|
+
short_description: str
|
|
38
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(
|
|
41
|
+
instance=instance,
|
|
42
|
+
seed=seed,
|
|
43
|
+
fixed_config=fixed_config,
|
|
44
|
+
level=level,
|
|
45
|
+
question=question,
|
|
46
|
+
dashboard_class=dashboard_class,
|
|
47
|
+
function=get_infeasible_form_config,
|
|
48
|
+
provide_reason=provide_reason,
|
|
49
|
+
)
|
|
50
|
+
self.task_description = ""
|
|
51
|
+
self.short_description = (
|
|
52
|
+
"Create an incident for the worst performing employee from the list."
|
|
53
|
+
)
|
|
54
|
+
self.filter_than = "lesser"
|
|
55
|
+
self.attribute_name = "assigned_to"
|
|
56
|
+
self.prefix = "ICI"
|
|
57
|
+
|
|
58
|
+
def set_compositional_task(self) -> None:
|
|
59
|
+
# The unique name for the user is created once the task is instantiated
|
|
60
|
+
base_user = table_api_call(
|
|
61
|
+
instance=self.instance,
|
|
62
|
+
table="sys_user",
|
|
63
|
+
params={
|
|
64
|
+
"sysparm_query": f"sys_id={self._base_user_sysid}",
|
|
65
|
+
},
|
|
66
|
+
)["result"][0]
|
|
67
|
+
self.user_name = base_user["first_name"] + " " + base_user["last_name"]
|
|
68
|
+
|
|
69
|
+
agent_full_names, agent_value_sysids = self.get_agent_values(
|
|
70
|
+
self.attribute_name, self.filter_than
|
|
71
|
+
)
|
|
72
|
+
self.agent_value_sysids = agent_value_sysids
|
|
73
|
+
incident_numbers = []
|
|
74
|
+
for _ in range(len(agent_full_names)):
|
|
75
|
+
incident_number = "INC" + str(random.randint(1000000, 9999999))
|
|
76
|
+
while (
|
|
77
|
+
incident_number in self.all_incident_numbers or incident_number in incident_numbers
|
|
78
|
+
):
|
|
79
|
+
incident_number = "INC" + str(random.randint(1000000, 9999999))
|
|
80
|
+
incident_numbers.append(incident_number)
|
|
81
|
+
|
|
82
|
+
self.incident_numbers = incident_numbers
|
|
83
|
+
|
|
84
|
+
create_incident_subtasks = []
|
|
85
|
+
|
|
86
|
+
for agent_full_name, incident_number in zip(agent_full_names, incident_numbers):
|
|
87
|
+
incident_config = {
|
|
88
|
+
"fields": {
|
|
89
|
+
"caller_id": "Caller",
|
|
90
|
+
"category": "Category",
|
|
91
|
+
"description": "Description",
|
|
92
|
+
"short_description": "Short description",
|
|
93
|
+
"impact": "Impact",
|
|
94
|
+
"number": "Number",
|
|
95
|
+
"urgency": "Urgency",
|
|
96
|
+
"assigned_to": "Assigned to",
|
|
97
|
+
},
|
|
98
|
+
"task_fields": [
|
|
99
|
+
"caller_id",
|
|
100
|
+
"category",
|
|
101
|
+
"description",
|
|
102
|
+
"short_description",
|
|
103
|
+
"impact",
|
|
104
|
+
"number",
|
|
105
|
+
"urgency",
|
|
106
|
+
"assigned_to",
|
|
107
|
+
],
|
|
108
|
+
"template_record": {
|
|
109
|
+
"caller_id": self.user_name,
|
|
110
|
+
"category": "Inquiry / Help",
|
|
111
|
+
"description": "Compulsory training for employee in probation",
|
|
112
|
+
"short_description": "Compulsory training for employee in probation",
|
|
113
|
+
"impact": "1 - High",
|
|
114
|
+
"number": incident_number,
|
|
115
|
+
"urgency": "1 - High",
|
|
116
|
+
"assigned_to": agent_full_name,
|
|
117
|
+
},
|
|
118
|
+
"infeasible_task_fields": [
|
|
119
|
+
"category",
|
|
120
|
+
"description",
|
|
121
|
+
"short_description",
|
|
122
|
+
"impact",
|
|
123
|
+
"number",
|
|
124
|
+
],
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
incident_config, self.infeasible_reasons = self.function(
|
|
128
|
+
config=incident_config, random=self.random
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
create_incident_subtask = [
|
|
132
|
+
# Navigate to the incident list
|
|
133
|
+
AllMenuTask(
|
|
134
|
+
instance=self.instance,
|
|
135
|
+
fixed_config={
|
|
136
|
+
"application": "Service Desk",
|
|
137
|
+
"module": "Incidents",
|
|
138
|
+
"url": "/now/nav/ui/classic/params/target/incident_list.do",
|
|
139
|
+
},
|
|
140
|
+
is_validated=False,
|
|
141
|
+
used_in_level_2=True,
|
|
142
|
+
has_description=False,
|
|
143
|
+
),
|
|
144
|
+
# Create an incident
|
|
145
|
+
CreateIncidentTask(
|
|
146
|
+
instance=self.instance,
|
|
147
|
+
fixed_config=incident_config,
|
|
148
|
+
is_validated=False,
|
|
149
|
+
used_in_level_2=True,
|
|
150
|
+
check_record_created=False,
|
|
151
|
+
has_description=True,
|
|
152
|
+
),
|
|
153
|
+
]
|
|
154
|
+
create_incident_subtasks += create_incident_subtask
|
|
155
|
+
|
|
156
|
+
self.compositional_task = create_incident_subtasks
|
|
157
|
+
|
|
158
|
+
def setup_goal(self, page: Page) -> tuple[str, dict]:
|
|
159
|
+
self.create_report()
|
|
160
|
+
self.set_compositional_task()
|
|
161
|
+
config = self.fixed_config if self.fixed_config else self._get_config()
|
|
162
|
+
incident_numbers_string = ", ".join(self.incident_numbers)
|
|
163
|
+
|
|
164
|
+
if self.level == 3:
|
|
165
|
+
self.task_description = (
|
|
166
|
+
self.task_description
|
|
167
|
+
+ f"Value to retrieve: {self.description_mapping[self.question]} of all the incidents. Comparator: Less than or equal to the value.\n"
|
|
168
|
+
+ f"Task: Create incidents with the following information: \n"
|
|
169
|
+
+ f"Category: 'Inquiry / Help', Impact: '1 - High', Urgency: '1 - High', Short description: 'Compulsory training for employee in probation', Caller: '{self.user_name}'.\n"
|
|
170
|
+
+ f"Assign the incidents you create to the agents mentioned above. You can use the incident numbers: {incident_numbers_string}, one for each."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
goal, info = super().setup_goal(
|
|
174
|
+
page=page, config=config, build_pretty_print_description=False
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if self.level == 2:
|
|
178
|
+
goal = (
|
|
179
|
+
self.task_description
|
|
180
|
+
+ f"\n1. Navigate to the CMDB reports and look for the report with the mentioned hashtag. \n"
|
|
181
|
+
+ f"\n2. Find the agents with number of incidents less than or equal to the {self.description_mapping[self.question]} of the incidents assigned to every one. \n"
|
|
182
|
+
+ f"\n3. Navigate to Service Desk > Incidents. \n"
|
|
183
|
+
+ f"\n4. Create new incidents with the following field values:- Category: 'Inquiry / Help', Impact: '1 - High', Urgency: '1 - High', Short description: 'Compulsory training for employee in probation' and assign them to each of the agents. You will create as many incidents as there are agents.\n"
|
|
184
|
+
+ f"\nYou should use the following incident numbers for each incident (one for each): {incident_numbers_string}."
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
return goal, info
|
|
188
|
+
|
|
189
|
+
def teardown(self) -> None:
|
|
190
|
+
for incident_number in self.incident_numbers:
|
|
191
|
+
created_incident_response = table_api_call(
|
|
192
|
+
instance=self.instance,
|
|
193
|
+
table="incident",
|
|
194
|
+
params={
|
|
195
|
+
"sysparm_query": f"number={incident_number}",
|
|
196
|
+
},
|
|
197
|
+
method="GET",
|
|
198
|
+
)["result"]
|
|
199
|
+
if len(created_incident_response) > 1:
|
|
200
|
+
raise Exception("Multiple incidents created")
|
|
201
|
+
if len(created_incident_response) == 1:
|
|
202
|
+
created_incident_sysid = created_incident_response[0]["sys_id"]
|
|
203
|
+
db_delete_from_table(
|
|
204
|
+
instance=self.instance,
|
|
205
|
+
table="incident",
|
|
206
|
+
sys_id=created_incident_sysid,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
return super().teardown()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class DashboardRetrieveIncidentAndMinCreateIncidentInfeasibleWithReasonTask(
|
|
213
|
+
DashboardRetrieveIncidentAndCreateIncidentInfeasibleTask, DashDoFinalTask
|
|
214
|
+
):
|
|
215
|
+
def __init__(
|
|
216
|
+
self,
|
|
217
|
+
instance: SNowInstance = None,
|
|
218
|
+
seed: int = None,
|
|
219
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
220
|
+
level: int = 3,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""
|
|
223
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
224
|
+
Attributes:
|
|
225
|
+
-----------
|
|
226
|
+
task_description: str
|
|
227
|
+
The start of the task description to be completed.
|
|
228
|
+
short_description: str
|
|
229
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
230
|
+
"""
|
|
231
|
+
super().__init__(
|
|
232
|
+
instance=instance,
|
|
233
|
+
seed=seed,
|
|
234
|
+
fixed_config=fixed_config,
|
|
235
|
+
level=level,
|
|
236
|
+
question="min",
|
|
237
|
+
dashboard_class=ReportMinMaxRetrievalTask,
|
|
238
|
+
provide_reason=True,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class DashboardRetrieveIncidentAndMinCreateIncidentInfeasibleTask(
|
|
243
|
+
DashboardRetrieveIncidentAndCreateIncidentInfeasibleTask, DashDoFinalTask
|
|
244
|
+
):
|
|
245
|
+
def __init__(
|
|
246
|
+
self,
|
|
247
|
+
instance: SNowInstance = None,
|
|
248
|
+
seed: int = None,
|
|
249
|
+
fixed_config: list[AbstractServiceNowTask] = None,
|
|
250
|
+
level: int = 2,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Retrieve the worst performing employee and create an incident to assign them a probation course.
|
|
254
|
+
Attributes:
|
|
255
|
+
-----------
|
|
256
|
+
task_description: str
|
|
257
|
+
The start of the task description to be completed.
|
|
258
|
+
short_description: str
|
|
259
|
+
A short description of the task to be completed. "Create an incident for the worst performing employee from the list"
|
|
260
|
+
"""
|
|
261
|
+
super().__init__(
|
|
262
|
+
instance=instance,
|
|
263
|
+
seed=seed,
|
|
264
|
+
fixed_config=fixed_config,
|
|
265
|
+
level=level,
|
|
266
|
+
question="min",
|
|
267
|
+
dashboard_class=ReportMinMaxRetrievalTask,
|
|
268
|
+
provide_reason=False,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
local_vars = locals().copy()
|
|
273
|
+
|
|
274
|
+
__TASKS__ = [
|
|
275
|
+
var
|
|
276
|
+
for var in local_vars.values()
|
|
277
|
+
if isinstance(var, type) and issubclass(var, DashDoFinalTask) and var is not DashDoFinalTask
|
|
278
|
+
]
|