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,499 @@
1
+ from faker import Faker
2
+
3
+ fake = Faker()
4
+
5
+ from playwright.sync_api._generated import Page
6
+
7
+ from .base import HumanEvalTask
8
+ from .filter_and_do import FilterAndDoTask
9
+
10
+ from ..mark_duplicate_problem import SetProblemAsDuplicateTask
11
+ from ..base import AbstractServiceNowTask
12
+
13
+ from ...api.problem import create_problem
14
+ from ...api.utils import db_delete_from_table, table_api_call
15
+ from ...config import (
16
+ # Expected columns for the different lists
17
+ EXPECTED_PROBLEM_COLUMNS_PATH,
18
+ )
19
+ from ...instance import SNowInstance
20
+
21
+
22
+ class FilterProblemsAndMarkDuplicatesTask(FilterAndDoTask):
23
+ """Basic task to filter problems with a specific hashtag and mark them as duplicates."""
24
+
25
+ def __init__(
26
+ self,
27
+ seed: int,
28
+ extra_problems: int,
29
+ navigation_config: dict,
30
+ instance: SNowInstance = None,
31
+ fixed_config: list[AbstractServiceNowTask] = None,
32
+ level: int = 2,
33
+ ) -> None:
34
+ super().__init__(
35
+ seed=seed,
36
+ instance=instance,
37
+ fixed_config=fixed_config,
38
+ navigation_config=navigation_config,
39
+ level=level,
40
+ protocol_name="Problem List Cleanup",
41
+ )
42
+ self.extra_problems = extra_problems # Number of non-duplicates to create; total problems will be extra_problems + 2
43
+ self.problem_priorities = [2, 2] + [
44
+ 1
45
+ ] * extra_problems # The first two problems will be duplicates with the same priority; the other ones will get top priority by default
46
+ self.problem_sys_ids = []
47
+ self.duplicate_problems = []
48
+
49
+ self.problem_hashtag = "#SERIES-" + self.unique_id[:10]
50
+ self.short_description = f"Clean-up your duplicate problems"
51
+ self.task_description = f'Referring to company protocol "{self.protocol_name}" (located in the "Company Protocols" knowledge base) clean-up your problem list (problems assigned to you) by marking duplicate problems among those with hashtag {self.problem_hashtag}.'
52
+
53
+ def _setup_list(self) -> None:
54
+ duplicated_short_descripton = f"{fake.sentence(4)}"
55
+
56
+ for i, priority in enumerate(self.problem_priorities):
57
+ # The first two problems will have the same short description; the other ones will get random ones
58
+ if i < 2:
59
+ short_description = duplicated_short_descripton
60
+ else:
61
+ short_description = None
62
+
63
+ problem_sys_id, problem_number = create_problem(
64
+ instance=self.instance,
65
+ problem_hashtag=self.problem_hashtag,
66
+ priority=priority,
67
+ user_sys_id=self._base_user_sysid,
68
+ short_description=short_description,
69
+ return_number=True,
70
+ )
71
+ self.problem_sys_ids.append(problem_sys_id)
72
+
73
+ if i < 2:
74
+ self.duplicate_problems.append({"number": problem_number, "sys_id": problem_sys_id})
75
+
76
+ self.filter_config = {
77
+ "list_url": "/now/nav/ui/classic/params/target/problem_list.do",
78
+ "expected_fields_path": EXPECTED_PROBLEM_COLUMNS_PATH,
79
+ "filter_columns": [
80
+ "short_description",
81
+ ],
82
+ "filter_kind": "AND",
83
+ "filter_operators": ["contains"],
84
+ "filter_values": [
85
+ f"{self.problem_hashtag}",
86
+ ],
87
+ }
88
+ # the 'tasks' attribute needs to be defined by children classes
89
+
90
+ def teardown(self) -> None:
91
+ for problem_sys_id in self.problem_sys_ids:
92
+ record_exists = table_api_call(
93
+ instance=self.instance,
94
+ table="problem",
95
+ params={"sysparm_query": f"sys_id={problem_sys_id}"},
96
+ )["result"]
97
+ if record_exists:
98
+ db_delete_from_table(
99
+ instance=self.instance,
100
+ table="problem",
101
+ sys_id=problem_sys_id,
102
+ )
103
+ super().teardown()
104
+
105
+
106
+ class BasicFilterProblemsAndMarkDuplicatesSmallTask(
107
+ FilterProblemsAndMarkDuplicatesTask, HumanEvalTask
108
+ ):
109
+ """Basic task to filter problems with a specific hashtag and mark them as duplicates. This"""
110
+
111
+ def __init__(
112
+ self,
113
+ seed: int,
114
+ instance: SNowInstance = None,
115
+ fixed_config: list[AbstractServiceNowTask] = None,
116
+ extra_problems: int = 2,
117
+ level: int = 2,
118
+ ) -> None:
119
+ super().__init__(
120
+ seed=seed,
121
+ instance=instance,
122
+ extra_problems=extra_problems,
123
+ fixed_config=fixed_config,
124
+ navigation_config={
125
+ "module": "Assigned to me",
126
+ "application": "Problem",
127
+ },
128
+ level=level,
129
+ )
130
+
131
+ def _setup_list(self) -> None:
132
+ super()._setup_list()
133
+ self.tasks = [
134
+ SetProblemAsDuplicateTask(
135
+ instance=self.instance,
136
+ fixed_config={
137
+ "target_problem": self.duplicate_problems[1],
138
+ "source_problem": self.duplicate_problems[0],
139
+ },
140
+ is_validated=True,
141
+ used_in_level_2=True,
142
+ goal_version="base",
143
+ level=self.level,
144
+ ),
145
+ ]
146
+
147
+
148
+ class BasicFilterProblemsAndMarkDuplicatesMediumTask(FilterProblemsAndMarkDuplicatesTask):
149
+ """Basic task to filter problems with a specific hashtag and mark them as duplicates. This"""
150
+
151
+ def __init__(
152
+ self,
153
+ seed: int,
154
+ instance: SNowInstance = None,
155
+ fixed_config: list[AbstractServiceNowTask] = None,
156
+ extra_problems: int = 4,
157
+ level: int = 2,
158
+ ) -> None:
159
+ super().__init__(
160
+ seed=seed,
161
+ instance=instance,
162
+ extra_problems=extra_problems,
163
+ fixed_config=fixed_config,
164
+ navigation_config={
165
+ "module": "Assigned to me",
166
+ "application": "Problem",
167
+ },
168
+ level=level,
169
+ )
170
+
171
+ def _setup_list(self) -> None:
172
+ super()._setup_list()
173
+ self.tasks = [
174
+ SetProblemAsDuplicateTask(
175
+ instance=self.instance,
176
+ fixed_config={
177
+ "target_problem": self.duplicate_problems[1],
178
+ "source_problem": self.duplicate_problems[0],
179
+ },
180
+ is_validated=True,
181
+ used_in_level_2=True,
182
+ goal_version="base",
183
+ level=self.level,
184
+ ),
185
+ ]
186
+
187
+
188
+ class BasicFilterProblemsAndMarkDuplicatesLargeTask(FilterProblemsAndMarkDuplicatesTask):
189
+ """Basic task to filter problems with a specific hashtag and mark them as duplicates. This"""
190
+
191
+ def __init__(
192
+ self,
193
+ seed: int,
194
+ instance: SNowInstance = None,
195
+ fixed_config: list[AbstractServiceNowTask] = None,
196
+ extra_problems: int = 6,
197
+ level: int = 2,
198
+ ) -> None:
199
+ super().__init__(
200
+ seed=seed,
201
+ instance=instance,
202
+ extra_problems=extra_problems,
203
+ fixed_config=fixed_config,
204
+ navigation_config={
205
+ "module": "Assigned to me",
206
+ "application": "Problem",
207
+ },
208
+ level=level,
209
+ )
210
+
211
+ def _setup_list(self) -> None:
212
+ super()._setup_list()
213
+ self.tasks = [
214
+ SetProblemAsDuplicateTask(
215
+ instance=self.instance,
216
+ fixed_config={
217
+ "target_problem": self.duplicate_problems[1],
218
+ "source_problem": self.duplicate_problems[0],
219
+ },
220
+ is_validated=True,
221
+ used_in_level_2=True,
222
+ goal_version="base",
223
+ level=self.level,
224
+ ),
225
+ ]
226
+
227
+
228
+ class PriorityFilterProblemsAndMarkDuplicatesSmallTask(
229
+ FilterProblemsAndMarkDuplicatesTask, HumanEvalTask
230
+ ):
231
+ """Task to filter problems with a specific hashtag and mark the least priority one as duplicate of the first."""
232
+
233
+ def __init__(
234
+ self,
235
+ seed: int,
236
+ instance: SNowInstance = None,
237
+ fixed_config: list[AbstractServiceNowTask] = None,
238
+ extra_problems: int = 2,
239
+ level: int = 2,
240
+ ) -> None:
241
+ super().__init__(
242
+ seed=seed,
243
+ instance=instance,
244
+ extra_problems=extra_problems,
245
+ fixed_config=fixed_config,
246
+ navigation_config={
247
+ "module": "All",
248
+ "application": "Problem",
249
+ },
250
+ level=level,
251
+ )
252
+ self.problem_priorities = [1, 2] + [1] * extra_problems
253
+
254
+ def _setup_list(self) -> None:
255
+ super()._setup_list()
256
+ self.tasks = [
257
+ SetProblemAsDuplicateTask(
258
+ instance=self.instance,
259
+ fixed_config={
260
+ "target_problem": self.duplicate_problems[1],
261
+ "source_problem": self.duplicate_problems[0],
262
+ },
263
+ respect_problem_ordering=True,
264
+ is_validated=True,
265
+ used_in_level_2=True,
266
+ goal_version="priority",
267
+ level=self.level,
268
+ ),
269
+ ]
270
+
271
+
272
+ class PriorityFilterProblemsAndMarkDuplicatesMediumTask(FilterProblemsAndMarkDuplicatesTask):
273
+ """Task to filter problems with a specific hashtag and mark the least priority one as duplicate of the first."""
274
+
275
+ def __init__(
276
+ self,
277
+ seed: int,
278
+ instance: SNowInstance = None,
279
+ fixed_config: list[AbstractServiceNowTask] = None,
280
+ extra_problems: int = 4,
281
+ level: int = 2,
282
+ ) -> None:
283
+ super().__init__(
284
+ seed=seed,
285
+ instance=instance,
286
+ extra_problems=extra_problems,
287
+ fixed_config=fixed_config,
288
+ navigation_config={
289
+ "module": "All",
290
+ "application": "Problem",
291
+ },
292
+ level=level,
293
+ )
294
+ self.problem_priorities = [1, 2] + [1] * extra_problems
295
+
296
+ def _setup_list(self) -> None:
297
+ super()._setup_list()
298
+ self.tasks = [
299
+ SetProblemAsDuplicateTask(
300
+ instance=self.instance,
301
+ fixed_config={
302
+ "target_problem": self.duplicate_problems[1],
303
+ "source_problem": self.duplicate_problems[0],
304
+ },
305
+ respect_problem_ordering=True,
306
+ is_validated=True,
307
+ used_in_level_2=True,
308
+ goal_version="priority",
309
+ level=self.level,
310
+ ),
311
+ ]
312
+
313
+
314
+ class PriorityFilterProblemsAndMarkDuplicatesLargeTask(FilterProblemsAndMarkDuplicatesTask):
315
+ """Task to filter problems with a specific hashtag and mark the least priority one as duplicate of the first."""
316
+
317
+ def __init__(
318
+ self,
319
+ seed: int,
320
+ instance: SNowInstance = None,
321
+ fixed_config: list[AbstractServiceNowTask] = None,
322
+ extra_problems: int = 6,
323
+ level: int = 2,
324
+ ) -> None:
325
+ super().__init__(
326
+ seed=seed,
327
+ instance=instance,
328
+ extra_problems=extra_problems,
329
+ fixed_config=fixed_config,
330
+ navigation_config={
331
+ "module": "All",
332
+ "application": "Problem",
333
+ },
334
+ level=level,
335
+ )
336
+ self.problem_priorities = [1, 2] + [1] * extra_problems
337
+
338
+ def _setup_list(self) -> None:
339
+ super()._setup_list()
340
+ self.tasks = [
341
+ SetProblemAsDuplicateTask(
342
+ instance=self.instance,
343
+ fixed_config={
344
+ "target_problem": self.duplicate_problems[1],
345
+ "source_problem": self.duplicate_problems[0],
346
+ },
347
+ respect_problem_ordering=True,
348
+ is_validated=True,
349
+ used_in_level_2=True,
350
+ goal_version="priority",
351
+ level=self.level,
352
+ ),
353
+ ]
354
+
355
+
356
+ class HighPriorityFilterProblemsAndMarkDuplicatesSmallTask(
357
+ FilterProblemsAndMarkDuplicatesTask, HumanEvalTask
358
+ ):
359
+ """Task to filter problems with a specific hashtag and mark high priority items as duplicates. As
360
+ a top priority item is marked as duplicate, we have to add a comment to it.
361
+ """
362
+
363
+ def __init__(
364
+ self,
365
+ seed: int,
366
+ instance: SNowInstance = None,
367
+ fixed_config: list[AbstractServiceNowTask] = None,
368
+ extra_problems: int = 2,
369
+ level: int = 2,
370
+ ) -> None:
371
+ super().__init__(
372
+ seed=seed,
373
+ instance=instance,
374
+ extra_problems=extra_problems,
375
+ fixed_config=fixed_config,
376
+ navigation_config={
377
+ "module": "All",
378
+ "application": "Problem",
379
+ },
380
+ level=level,
381
+ )
382
+ self.problem_priorities = [1, 1] + [1] * extra_problems
383
+
384
+ def _setup_list(self) -> None:
385
+ super()._setup_list()
386
+ self.tasks = [
387
+ SetProblemAsDuplicateTask(
388
+ instance=self.instance,
389
+ fixed_config={
390
+ "target_problem": self.duplicate_problems[1],
391
+ "source_problem": self.duplicate_problems[0],
392
+ },
393
+ respect_problem_ordering=False,
394
+ is_validated=True,
395
+ used_in_level_2=True,
396
+ goal_version="high priority",
397
+ level=self.level,
398
+ ),
399
+ ]
400
+
401
+
402
+ class HighPriorityFilterProblemsAndMarkDuplicatesMediumTask(FilterProblemsAndMarkDuplicatesTask):
403
+ """Task to filter problems with a specific hashtag and mark high priority items as duplicates. As
404
+ a top priority item is marked as duplicate, we have to add a comment to it.
405
+ """
406
+
407
+ def __init__(
408
+ self,
409
+ seed: int,
410
+ instance: SNowInstance = None,
411
+ fixed_config: list[AbstractServiceNowTask] = None,
412
+ extra_problems: int = 4,
413
+ level: int = 2,
414
+ ) -> None:
415
+ super().__init__(
416
+ seed=seed,
417
+ instance=instance,
418
+ extra_problems=extra_problems,
419
+ fixed_config=fixed_config,
420
+ navigation_config={
421
+ "module": "All",
422
+ "application": "Problem",
423
+ },
424
+ level=level,
425
+ )
426
+ self.problem_priorities = [1, 1] + [1] * extra_problems
427
+
428
+ def _setup_list(self) -> None:
429
+ super()._setup_list()
430
+ self.tasks = [
431
+ SetProblemAsDuplicateTask(
432
+ instance=self.instance,
433
+ fixed_config={
434
+ "target_problem": self.duplicate_problems[1],
435
+ "source_problem": self.duplicate_problems[0],
436
+ },
437
+ respect_problem_ordering=False,
438
+ is_validated=True,
439
+ used_in_level_2=True,
440
+ goal_version="high priority",
441
+ level=self.level,
442
+ ),
443
+ ]
444
+
445
+
446
+ class HighPriorityFilterProblemsAndMarkDuplicatesLargeTask(FilterProblemsAndMarkDuplicatesTask):
447
+ """Task to filter problems with a specific hashtag and mark high priority items as duplicates. As
448
+ a top priority item is marked as duplicate, we have to add a comment to it.
449
+ """
450
+
451
+ def __init__(
452
+ self,
453
+ seed: int,
454
+ instance: SNowInstance = None,
455
+ fixed_config: list[AbstractServiceNowTask] = None,
456
+ extra_problems: int = 6,
457
+ level: int = 2,
458
+ ) -> None:
459
+ super().__init__(
460
+ seed=seed,
461
+ instance=instance,
462
+ extra_problems=extra_problems,
463
+ fixed_config=fixed_config,
464
+ navigation_config={
465
+ "module": "All",
466
+ "application": "Problem",
467
+ },
468
+ level=level,
469
+ )
470
+ self.problem_priorities = [1, 1] + [1] * extra_problems
471
+
472
+ def _setup_list(self) -> None:
473
+ super()._setup_list()
474
+ self.tasks = [
475
+ SetProblemAsDuplicateTask(
476
+ instance=self.instance,
477
+ fixed_config={
478
+ "target_problem": self.duplicate_problems[1],
479
+ "source_problem": self.duplicate_problems[0],
480
+ },
481
+ respect_problem_ordering=False,
482
+ is_validated=True,
483
+ used_in_level_2=True,
484
+ goal_version="high priority",
485
+ level=self.level,
486
+ ),
487
+ ]
488
+
489
+
490
+ local_vars = locals().copy()
491
+
492
+ __TASKS__ = [
493
+ var
494
+ for var in local_vars.values()
495
+ if isinstance(var, type)
496
+ and issubclass(var, FilterAndDoTask)
497
+ and var is not FilterAndDoTask
498
+ and var is not FilterProblemsAndMarkDuplicatesTask
499
+ ]