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.
Files changed (91) hide show
  1. browsergym/workarena/__init__.py +13 -1
  2. browsergym/workarena/api/category.py +74 -0
  3. browsergym/workarena/api/change_request.py +87 -0
  4. browsergym/workarena/api/computer_asset.py +90 -0
  5. browsergym/workarena/api/cost_center.py +19 -0
  6. browsergym/workarena/api/expense_line.py +89 -0
  7. browsergym/workarena/api/incident.py +45 -0
  8. browsergym/workarena/api/knowledge.py +29 -0
  9. browsergym/workarena/api/problem.py +90 -0
  10. browsergym/workarena/api/report.py +183 -0
  11. browsergym/workarena/api/requested_items.py +63 -0
  12. browsergym/workarena/api/user.py +11 -8
  13. browsergym/workarena/api/utils.py +47 -3
  14. browsergym/workarena/config.py +21 -1
  15. browsergym/workarena/data_files/setup_files/forms/expected_incident_form_fields.json +1 -1
  16. browsergym/workarena/data_files/setup_files/forms/expected_request_item_form_fields.json +1 -0
  17. browsergym/workarena/data_files/setup_files/knowledge/protocols.json +46 -0
  18. browsergym/workarena/data_files/setup_files/knowledge/test.html +1 -0
  19. browsergym/workarena/data_files/setup_files/lists/expected_asset_list_columns.json +2 -24
  20. browsergym/workarena/data_files/setup_files/lists/expected_change_request_list_columns.json +4 -40
  21. browsergym/workarena/data_files/setup_files/lists/expected_expense_line_list_columns.json +12 -0
  22. browsergym/workarena/data_files/setup_files/lists/expected_hardware_list_columns.json +1 -42
  23. browsergym/workarena/data_files/setup_files/lists/expected_incident_list_columns.json +2 -18
  24. browsergym/workarena/data_files/setup_files/lists/expected_problem_list_columns.json +12 -0
  25. browsergym/workarena/data_files/setup_files/lists/expected_requested_items_list_columns.json +12 -0
  26. browsergym/workarena/data_files/setup_files/lists/expected_service_catalog_list_columns.json +2 -19
  27. browsergym/workarena/data_files/setup_files/lists/expected_user_list_columns.json +3 -50
  28. browsergym/workarena/data_files/task_configs/all_menu.json +95 -95
  29. browsergym/workarena/data_files/task_configs/dashboard_retrieval_minmax_task.json +1 -1
  30. browsergym/workarena/data_files/task_configs/dashboard_retrieval_value_task.json +1 -1
  31. browsergym/workarena/data_files/task_configs/filter_service_catalog_item_list_task.json +7986 -7982
  32. browsergym/workarena/data_files/task_configs/impersonation_users.json +3 -3
  33. browsergym/workarena/data_files/task_configs/report_retrieval_minmax_task.json +1 -1
  34. browsergym/workarena/data_files/task_configs/report_retrieval_value_task.json +1 -1
  35. browsergym/workarena/human_eval/console.js +176 -0
  36. browsergym/workarena/human_eval/tool.py +366 -0
  37. browsergym/workarena/install.py +81 -20
  38. browsergym/workarena/tasks/base.py +55 -20
  39. browsergym/workarena/tasks/comp_building_block.py +4 -0
  40. browsergym/workarena/tasks/compositional/__init__.py +76 -0
  41. browsergym/workarena/tasks/compositional/base.py +364 -0
  42. browsergym/workarena/tasks/compositional/dash_do_base.py +1366 -0
  43. browsergym/workarena/tasks/compositional/dash_do_catalog.py +1127 -0
  44. browsergym/workarena/tasks/compositional/dash_do_catalog_infeasible.py +2047 -0
  45. browsergym/workarena/tasks/compositional/dash_do_create_incident.py +403 -0
  46. browsergym/workarena/tasks/compositional/dash_do_create_incident_infeasible.py +278 -0
  47. browsergym/workarena/tasks/compositional/dash_do_create_problem.py +336 -0
  48. browsergym/workarena/tasks/compositional/dash_do_create_problem_infeasible.py +235 -0
  49. browsergym/workarena/tasks/compositional/dash_do_filter.py +1600 -0
  50. browsergym/workarena/tasks/compositional/dash_do_request_item.py +1315 -0
  51. browsergym/workarena/tasks/compositional/dash_do_request_item_infeasible.py +693 -0
  52. browsergym/workarena/tasks/compositional/delete_record.py +341 -0
  53. browsergym/workarena/tasks/compositional/edit_knowledge_base.py +457 -0
  54. browsergym/workarena/tasks/compositional/expense_management.py +598 -0
  55. browsergym/workarena/tasks/compositional/filter_and_do.py +139 -0
  56. browsergym/workarena/tasks/compositional/find_and_order_item.py +345 -0
  57. browsergym/workarena/tasks/compositional/manage_change_request_schedule.py +1417 -0
  58. browsergym/workarena/tasks/compositional/mark_duplicate_problems.py +499 -0
  59. browsergym/workarena/tasks/compositional/maximize_investment_return.py +1763 -0
  60. browsergym/workarena/tasks/compositional/navigate_and_do.py +1151 -0
  61. browsergym/workarena/tasks/compositional/navigate_and_do_infeasible.py +2100 -0
  62. browsergym/workarena/tasks/compositional/offboard_user.py +207 -0
  63. browsergym/workarena/tasks/compositional/onboard_user.py +226 -0
  64. browsergym/workarena/tasks/compositional/update_task.py +145 -0
  65. browsergym/workarena/tasks/compositional/utils/curriculum.py +215 -0
  66. browsergym/workarena/tasks/compositional/utils/infeasible_configs.py +151 -0
  67. browsergym/workarena/tasks/compositional/utils/knapsack.py +192 -0
  68. browsergym/workarena/tasks/compositional/warranty_check.py +227 -0
  69. browsergym/workarena/tasks/compositional/work_assignment.py +804 -0
  70. browsergym/workarena/tasks/compositional/workload_balancing.py +396 -0
  71. browsergym/workarena/tasks/dashboard.py +188 -8
  72. browsergym/workarena/tasks/form.py +1024 -232
  73. browsergym/workarena/tasks/knowledge.py +216 -25
  74. browsergym/workarena/tasks/list.py +519 -102
  75. browsergym/workarena/tasks/mark_duplicate_problem.py +171 -0
  76. browsergym/workarena/tasks/navigation.py +55 -13
  77. browsergym/workarena/tasks/scripts/extract_all_menu_items.py +9 -2
  78. browsergym/workarena/tasks/scripts/generate_dashboard_configs.py +6 -5
  79. browsergym/workarena/tasks/scripts/service_catalog.py +2 -1
  80. browsergym/workarena/tasks/scripts/validate.py +8 -2
  81. browsergym/workarena/tasks/send_chat_message.py +90 -0
  82. browsergym/workarena/tasks/service_catalog.py +94 -26
  83. browsergym/workarena/tasks/utils/form.py +1 -4
  84. browsergym/workarena/tasks/utils/private_tasks.py +63 -0
  85. browsergym/workarena/tasks/utils/utils.py +13 -0
  86. {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/METADATA +27 -20
  87. browsergym_workarena-0.3.0.dist-info/RECORD +138 -0
  88. {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/entry_points.txt +1 -0
  89. browsergym_workarena-0.2.0.dist-info/RECORD +0 -85
  90. {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/WHEEL +0 -0
  91. {browsergym_workarena-0.2.0.dist-info → browsergym_workarena-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,139 @@
1
+ from faker import Faker
2
+
3
+ fake = Faker()
4
+
5
+ from playwright.sync_api._generated import Page
6
+
7
+ from browsergym.workarena.tasks.knowledge import KnowledgeBaseSearchTask
8
+ from browsergym.workarena.tasks.list import FilterListTask
9
+ from browsergym.workarena.tasks.navigation import AllMenuTask
10
+
11
+ from .base import CompositionalTask
12
+
13
+ from ..base import AbstractServiceNowTask
14
+
15
+ from ...instance import SNowInstance
16
+
17
+
18
+ class FilterAndDoTask(CompositionalTask):
19
+ def __init__(
20
+ self,
21
+ seed: int,
22
+ instance: SNowInstance = None,
23
+ fixed_config: list[AbstractServiceNowTask] = None,
24
+ navigation_config: dict = None,
25
+ protocol_name: str = None,
26
+ level: int = 2,
27
+ ) -> None:
28
+ """
29
+ Generic task to navigate to a specific page, run a filter and perform a task.
30
+
31
+ Parameters:
32
+ -----------
33
+ instance: SNowInstance
34
+ The ServiceNow instance to run the task on.
35
+ fixed_config: list[AbstractServiceNowTask]
36
+ A list of tuples, each containing a subtask
37
+ navigation_config: dict
38
+ Configuration to use for the navigation to the list that will be filtered. Contains application and module.
39
+ URL is not necessary as the navigation steps are not validated
40
+ protocol_name: str
41
+ The name of the protocol to refer to in the task description for L3.
42
+ level: int
43
+ The level of the task; choice between 2 and 3. L2 will have all the info in the the goal and start in the SNOW home page.
44
+ L3 will start in a private task page describing the information needed to complete the task and the related company protocol
45
+ to complete it.
46
+
47
+ Attributes:
48
+ -----------
49
+ filter_config: dict
50
+ Configuration to use for the filter that will be applied to the list. Contains filter_columns, filter_values and filter_kind in addition to the path to the expected fields in the list.
51
+ this is set by the _setup_list method.
52
+ tasks: List[AbstractServiceNowTask]
53
+ The tasks to perform after having filtered the list. Set by the child setup
54
+ task_description: str
55
+ The start of the task description to be completed. Provided by the child class.
56
+ short_description: str
57
+ A short description of the task to be completed. "Create a new user". Provided by the child class.
58
+ """
59
+ assert level in [2, 3], "Level must be either 2 or 3"
60
+ self.level = level
61
+ super().__init__(
62
+ seed=seed,
63
+ instance=instance,
64
+ fixed_config=fixed_config,
65
+ level=level,
66
+ )
67
+ self.used_in_level_2 = self.level == 2
68
+ self.navigation_config = navigation_config
69
+ self.filter_config = None
70
+ self.protocol_name = protocol_name
71
+ self.task_description = None
72
+ self.short_description = None
73
+ self.tasks = []
74
+
75
+ def _setup_list(self) -> None:
76
+ """Used to create the necessary records in the list + setting up the list filter attribute before filtering it."""
77
+ raise NotImplementedError
78
+
79
+ def setup_goal(self, page: Page) -> tuple[str, dict]:
80
+ self._setup_list()
81
+ config = self.fixed_config if self.fixed_config else self._get_config()
82
+
83
+ goal, info = super().setup_goal(page=page, config=config)
84
+
85
+ return goal, info
86
+
87
+ def _get_config(self) -> list[AbstractServiceNowTask]:
88
+ list_url = self.filter_config["list_url"]
89
+
90
+ navigate_to_protocol_config = [
91
+ # Navigate to the KB
92
+ AllMenuTask(
93
+ instance=self.instance,
94
+ fixed_config={
95
+ "application": "Self-Service",
96
+ "module": "Knowledge",
97
+ "url": "/now/nav/ui/classic/params/target/%24knowledge.do",
98
+ },
99
+ is_validated=False,
100
+ used_in_level_2=False,
101
+ ),
102
+ # Find the protocol for on-boarding a new user
103
+ KnowledgeBaseSearchTask(
104
+ instance=self.instance,
105
+ fixed_config={
106
+ "alternative_answers": [],
107
+ "item": f"{self.protocol_name}",
108
+ "question": f'Can you find the "{self.protocol_name}" Protocol in the Knowledge Base?',
109
+ "value": "",
110
+ },
111
+ is_validated=False,
112
+ used_in_level_2=False,
113
+ ),
114
+ ]
115
+
116
+ config = [
117
+ # Navigate to the task start page
118
+ AllMenuTask(
119
+ instance=self.instance,
120
+ fixed_config=self.navigation_config,
121
+ is_validated=False,
122
+ used_in_level_2=True,
123
+ ),
124
+ # Filter the the list; the config it uses is set by the _setup_list method
125
+ FilterListTask(
126
+ instance=self.instance,
127
+ list_url=list_url,
128
+ fixed_config=self.filter_config,
129
+ expected_fields_path=self.filter_config["expected_fields_path"],
130
+ is_validated=False,
131
+ used_in_level_2=True,
132
+ ),
133
+ ] + self.tasks
134
+
135
+ # To support the option of having no protocol
136
+ if self.protocol_name:
137
+ config = navigate_to_protocol_config + config
138
+
139
+ return config
@@ -0,0 +1,345 @@
1
+ from faker import Faker
2
+
3
+ fake = Faker()
4
+
5
+ from browsergym.workarena.tasks.navigation import AllMenuTask
6
+ from browsergym.workarena.tasks.send_chat_message import SendChatMessageGenericTask
7
+ from browsergym.workarena.tasks.service_catalog import (
8
+ OrderDeveloperLaptopTask,
9
+ OrderIpadMiniTask,
10
+ OrderIpadProTask,
11
+ OrderSalesLaptopTask,
12
+ OrderStandardLaptopTask,
13
+ OrderAppleWatchTask,
14
+ OrderAppleMacBookPro15Task,
15
+ OrderDevelopmentLaptopPCTask,
16
+ OrderLoanerLaptopTask,
17
+ )
18
+
19
+ from .base import HumanEvalTask
20
+ from .filter_and_do import FilterAndDoTask
21
+
22
+ from ..base import AbstractServiceNowTask
23
+
24
+ from ...api.requested_items import create_requested_item
25
+ from ...api.user import create_user
26
+ from ...api.utils import db_delete_from_table, table_api_call
27
+ from ...config import (
28
+ # Expected columns for the different lists
29
+ EXPECTED_REQUESTED_ITEMS_COLUMNS_PATH,
30
+ )
31
+ from ...instance import SNowInstance
32
+
33
+
34
+ class FilterRequestedItemsAndOrderCatalogItemTask(FilterAndDoTask, HumanEvalTask):
35
+ """Generic task to filter the requested items list to find what a given user has requested and order the same thing.
36
+ Args:
37
+ fixed_request_item: str
38
+ The requested item to find and order.
39
+ task_class: AbstractServiceNowTask
40
+ The class of the task to order the item.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ seed: int,
46
+ instance: SNowInstance = None,
47
+ fixed_config: list[AbstractServiceNowTask] = None,
48
+ fixed_request_item: str = None,
49
+ order_task_class: AbstractServiceNowTask = None,
50
+ level: int = 2,
51
+ ) -> None:
52
+ super().__init__(
53
+ seed=seed,
54
+ instance=instance,
55
+ fixed_config=fixed_config,
56
+ navigation_config={
57
+ "module": "Requested Items",
58
+ "application": "Self-Service",
59
+ },
60
+ level=level,
61
+ protocol_name="",
62
+ )
63
+ self.fixed_request_item = fixed_request_item
64
+ self.order_task_class = order_task_class
65
+ # name of the user the item will be assigned to
66
+ self.user_full_name = (
67
+ fake.first_name()
68
+ + "-"
69
+ + fake.first_name()
70
+ + " "
71
+ + fake.last_name()
72
+ + "-"
73
+ + fake.last_name()
74
+ )
75
+ self.short_description = f"Order same item as {self.user_full_name}"
76
+ self.task_description = f'{self.user_full_name} has recently requested an item from the service catalog. You need to order the same. Find what it is from the "Requested Items" list and order it from the service catalog. If possible, set the item\'s configuration to match the following: \n'
77
+ self.created_user_sys_id = None # sys_id of the user to assign the item to
78
+ self.requested_item_sys_id = None # sys_id of the requested item to order
79
+ self.tasks = []
80
+
81
+ def _setup_list(self) -> None:
82
+ self.filter_config = {
83
+ "list_url": "/now/nav/ui/classic/params/target/sc_req_item_list.do",
84
+ "expected_fields_path": EXPECTED_REQUESTED_ITEMS_COLUMNS_PATH,
85
+ "filter_columns": [
86
+ "requested_for",
87
+ ],
88
+ "filter_kind": "AND",
89
+ "filter_operators": ["contains"],
90
+ "filter_values": [
91
+ f"{self.user_full_name}",
92
+ ],
93
+ }
94
+ # Create a new user to assign the item to
95
+ first_name = self.user_full_name.split(" ")[0]
96
+ last_name = self.user_full_name.split(" ")[1]
97
+ _, _, self.created_user_sys_id = create_user(
98
+ self.instance, first_name=first_name, last_name=last_name, random=self.random
99
+ )
100
+ # Create a new requested item to order
101
+ self.requested_item_sys_id, _ = create_requested_item(
102
+ self.instance,
103
+ system_name=self.fixed_request_item,
104
+ user_sys_id=self.created_user_sys_id,
105
+ )
106
+ self.tasks.append(
107
+ # After the filter has been made and the information retrieved, navigate to the catalog
108
+ AllMenuTask(
109
+ instance=self.instance,
110
+ fixed_config={
111
+ "module": "Service Catalog",
112
+ "application": "Self-Service",
113
+ },
114
+ used_in_level_2=True,
115
+ is_validated=False,
116
+ )
117
+ )
118
+ self.tasks.append(
119
+ SendChatMessageGenericTask(
120
+ instance=self.instance,
121
+ message="a",
122
+ answer_format="a",
123
+ level=self.level,
124
+ description=f"Clear the existing filters on the page. \n",
125
+ is_validated=False,
126
+ use_description_in_l3=True,
127
+ used_in_level_2=True,
128
+ )
129
+ )
130
+ order_task_config = self.random.choice(self.order_task_class.all_configs())
131
+ # task to order the item
132
+ item_order_task = self.order_task_class(
133
+ seed=self.seed,
134
+ instance=self.instance,
135
+ fixed_config=order_task_config,
136
+ used_in_level_2=True,
137
+ is_validated=True,
138
+ config_only_in_desc=True,
139
+ )
140
+ self.tasks.append(item_order_task)
141
+
142
+ def teardown(self) -> None:
143
+ # Delete the requested item and the user if they exist
144
+ requested_item_exists = table_api_call(
145
+ instance=self.instance,
146
+ table="sc_req_item",
147
+ params={"sysparm_query": f"sys_id={self.requested_item_sys_id}"},
148
+ )["result"]
149
+ if requested_item_exists:
150
+ db_delete_from_table(
151
+ instance=self.instance,
152
+ table="sc_req_item",
153
+ sys_id=self.requested_item_sys_id,
154
+ )
155
+ user_exists = table_api_call(
156
+ instance=self.instance,
157
+ table="sys_user",
158
+ params={"sysparm_query": f"sys_id={self.created_user_sys_id}"},
159
+ )["result"]
160
+ if user_exists:
161
+ db_delete_from_table(
162
+ instance=self.instance,
163
+ table="sys_user",
164
+ sys_id=self.created_user_sys_id,
165
+ )
166
+
167
+ super().teardown()
168
+
169
+
170
+ class FilterRequestedItemsAndOrderDeveloperLaptopTask(FilterRequestedItemsAndOrderCatalogItemTask):
171
+ def __init__(
172
+ self,
173
+ seed: int,
174
+ instance: SNowInstance = None,
175
+ fixed_config: list[AbstractServiceNowTask] = None,
176
+ level: int = 2,
177
+ ) -> None:
178
+ super().__init__(
179
+ seed=seed,
180
+ instance=instance,
181
+ fixed_config=fixed_config,
182
+ fixed_request_item="Developer Laptop (Mac)",
183
+ level=level,
184
+ order_task_class=OrderDeveloperLaptopTask,
185
+ )
186
+
187
+
188
+ class FilterRequestedItemsAndOrderIpadMiniTask(FilterRequestedItemsAndOrderCatalogItemTask):
189
+ def __init__(
190
+ self,
191
+ seed: int,
192
+ instance: SNowInstance = None,
193
+ fixed_config: list[AbstractServiceNowTask] = None,
194
+ level: int = 2,
195
+ ) -> None:
196
+ super().__init__(
197
+ seed=seed,
198
+ instance=instance,
199
+ fixed_config=fixed_config,
200
+ fixed_request_item="iPad mini",
201
+ level=level,
202
+ order_task_class=OrderIpadMiniTask,
203
+ )
204
+
205
+
206
+ class FilterRequestedItemsAndOrderIpadProTask(FilterRequestedItemsAndOrderCatalogItemTask):
207
+ def __init__(
208
+ self,
209
+ seed: int,
210
+ instance: SNowInstance = None,
211
+ fixed_config: list[AbstractServiceNowTask] = None,
212
+ level: int = 2,
213
+ ) -> None:
214
+ super().__init__(
215
+ seed=seed,
216
+ instance=instance,
217
+ fixed_config=fixed_config,
218
+ fixed_request_item="iPad pro",
219
+ level=level,
220
+ order_task_class=OrderIpadProTask,
221
+ )
222
+
223
+
224
+ class FilterRequestedItemsAndOrderSalesLaptopTask(FilterRequestedItemsAndOrderCatalogItemTask):
225
+ def __init__(
226
+ self,
227
+ seed: int,
228
+ instance: SNowInstance = None,
229
+ fixed_config: list[AbstractServiceNowTask] = None,
230
+ level: int = 2,
231
+ ) -> None:
232
+ super().__init__(
233
+ seed=seed,
234
+ instance=instance,
235
+ fixed_config=fixed_config,
236
+ fixed_request_item="Sales Laptop",
237
+ level=level,
238
+ order_task_class=OrderSalesLaptopTask,
239
+ )
240
+
241
+
242
+ class FilterRequestedItemsAndOrderStandardLaptopTask(FilterRequestedItemsAndOrderCatalogItemTask):
243
+ def __init__(
244
+ self,
245
+ seed: int,
246
+ instance: SNowInstance = None,
247
+ fixed_config: list[AbstractServiceNowTask] = None,
248
+ level: int = 2,
249
+ ) -> None:
250
+ super().__init__(
251
+ seed=seed,
252
+ instance=instance,
253
+ fixed_config=fixed_config,
254
+ fixed_request_item="Standard Laptop",
255
+ level=level,
256
+ order_task_class=OrderStandardLaptopTask,
257
+ )
258
+
259
+
260
+ class FilterRequestedItemsAndOrderAppleWatchTask(FilterRequestedItemsAndOrderCatalogItemTask):
261
+ def __init__(
262
+ self,
263
+ seed: int,
264
+ instance: SNowInstance = None,
265
+ fixed_config: list[AbstractServiceNowTask] = None,
266
+ level: int = 2,
267
+ ) -> None:
268
+ super().__init__(
269
+ seed=seed,
270
+ instance=instance,
271
+ fixed_config=fixed_config,
272
+ fixed_request_item="Apple Watch",
273
+ level=level,
274
+ order_task_class=OrderAppleWatchTask,
275
+ )
276
+
277
+
278
+ class FilterRequestedItemsAndOrderAppleMacBookPro15Task(
279
+ FilterRequestedItemsAndOrderCatalogItemTask
280
+ ):
281
+ def __init__(
282
+ self,
283
+ seed: int,
284
+ instance: SNowInstance = None,
285
+ fixed_config: list[AbstractServiceNowTask] = None,
286
+ level: int = 2,
287
+ ) -> None:
288
+ super().__init__(
289
+ seed=seed,
290
+ instance=instance,
291
+ fixed_config=fixed_config,
292
+ fixed_request_item='Apple MacBook Pro 15"',
293
+ level=level,
294
+ order_task_class=OrderAppleMacBookPro15Task,
295
+ )
296
+
297
+
298
+ class FilterRequestedItemsAndOrderDevelopmentLaptopPCTask(
299
+ FilterRequestedItemsAndOrderCatalogItemTask
300
+ ):
301
+ def __init__(
302
+ self,
303
+ seed: int,
304
+ instance: SNowInstance = None,
305
+ fixed_config: list[AbstractServiceNowTask] = None,
306
+ level: int = 2,
307
+ ) -> None:
308
+ super().__init__(
309
+ seed=seed,
310
+ instance=instance,
311
+ fixed_config=fixed_config,
312
+ fixed_request_item="Development Laptop (PC)",
313
+ level=level,
314
+ order_task_class=OrderDevelopmentLaptopPCTask,
315
+ )
316
+
317
+
318
+ class FilterRequestedItemsAndOrderLoanerLaptopTask(FilterRequestedItemsAndOrderCatalogItemTask):
319
+ def __init__(
320
+ self,
321
+ seed: int,
322
+ instance: SNowInstance = None,
323
+ fixed_config: list[AbstractServiceNowTask] = None,
324
+ level: int = 2,
325
+ ) -> None:
326
+ super().__init__(
327
+ seed=seed,
328
+ instance=instance,
329
+ fixed_config=fixed_config,
330
+ fixed_request_item="Notebook Computer Loaner",
331
+ level=level,
332
+ order_task_class=OrderLoanerLaptopTask,
333
+ )
334
+
335
+
336
+ local_vars = locals().copy()
337
+
338
+ __TASKS__ = [
339
+ var
340
+ for var in local_vars.values()
341
+ if isinstance(var, type)
342
+ and issubclass(var, FilterAndDoTask)
343
+ and var is not FilterAndDoTask
344
+ and var is not FilterRequestedItemsAndOrderCatalogItemTask
345
+ ]