browsergym-workarena 0.4.3__py3-none-any.whl → 0.5.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.
@@ -1,3 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
1
4
  import html
2
5
  import json
3
6
  import logging
@@ -10,6 +13,7 @@ from tenacity import retry, stop_after_attempt, retry_if_exception_type
10
13
  from requests import HTTPError
11
14
  from time import sleep
12
15
 
16
+ from .api.system_properties import get_sys_property, set_sys_property
13
17
  from .api.ui_themes import get_workarena_theme_variants
14
18
  from .api.user import create_user
15
19
  from .api.utils import table_api_call, table_column_info
@@ -38,7 +42,7 @@ from .config import (
38
42
  EXPECTED_USER_FORM_FIELDS_PATH,
39
43
  # Patch flag for reports
40
44
  REPORT_PATCH_FLAG,
41
- REPORT_DATE_FILTER,
45
+ REPORT_FILTER_PROPERTY,
42
46
  # Supported ServiceNow releases
43
47
  SNOW_SUPPORTED_RELEASES,
44
48
  # For workflows setup
@@ -47,55 +51,52 @@ from .config import (
47
51
  UI_THEMES_UPDATE_SET,
48
52
  )
49
53
  from .api.user import set_user_preference
50
- from .instance import SNowInstance
54
+ from .instance import SNowInstance as _BaseSNowInstance
51
55
  from .utils import url_login
52
56
 
53
57
 
54
- def _set_sys_property(property_name: str, value: str):
55
- """
56
- Set a sys_property in the instance.
58
+ _CLI_INSTANCE_URL: str | None = None
59
+ _CLI_INSTANCE_PASSWORD: str | None = None
60
+
57
61
 
62
+ def SNowInstance(snow_credentials: tuple[str, str] | None = None):
58
63
  """
59
- instance = SNowInstance()
64
+ Wrapper around the standard SNowInstance that uses CLI-provided instance URL and password if none are provided.
65
+ """
66
+ if not _CLI_INSTANCE_URL:
67
+ raise RuntimeError("Installer requires --instance-url to create a SNowInstance.")
60
68
 
61
- property = table_api_call(
62
- instance=instance,
63
- table="sys_properties",
64
- params={"sysparm_query": f"name={property_name}", "sysparm_fields": "sys_id"},
65
- )["result"]
69
+ resolved_creds = snow_credentials
66
70
 
67
- if not property:
68
- property_sysid = ""
69
- method = "POST"
70
- else:
71
- property_sysid = "/" + property[0]["sys_id"]
72
- method = "PUT"
71
+ if resolved_creds is None:
72
+ if not _CLI_INSTANCE_PASSWORD:
73
+ raise RuntimeError(
74
+ "Installer requires --instance-password (or explicit credentials) to create a SNowInstance."
75
+ )
76
+ resolved_creds = ("admin", _CLI_INSTANCE_PASSWORD)
73
77
 
74
- property = table_api_call(
75
- instance=instance,
76
- table=f"sys_properties{property_sysid}",
77
- method=method,
78
- json={"name": property_name, "value": value},
78
+ return _BaseSNowInstance(
79
+ snow_url=_CLI_INSTANCE_URL,
80
+ snow_credentials=resolved_creds,
79
81
  )
80
82
 
81
- # Verify that the property was updated
82
- assert property["result"]["value"] == value, f"Error setting {property_name}."
83
-
84
83
 
85
- def _get_sys_property(property_name: str) -> str:
84
+ def _is_dev_portal_instance() -> bool:
86
85
  """
87
- Get a sys_property from the instance.
86
+ Check if the instance is a ServiceNow Developer Portal instance.
87
+
88
+ Returns:
89
+ --------
90
+ bool: True if the instance is a developer portal instance, False otherwise.
88
91
 
89
92
  """
90
93
  instance = SNowInstance()
91
-
92
- property_value = table_api_call(
93
- instance=instance,
94
- table="sys_properties",
95
- params={"sysparm_query": f"name={property_name}", "sysparm_fields": "value"},
96
- )["result"][0]["value"]
97
-
98
- return property_value
94
+ # Check if the instance url has the for devXXXXXX.service-now.com format (where X is a digit)
95
+ if re.match(r"^https?://dev\d{6}\.service-now\.com", instance.snow_url):
96
+ logging.info("Detected a developer portal instance...")
97
+ return True
98
+ logging.info("Detected an internal instance...")
99
+ return False
99
100
 
100
101
 
101
102
  def _install_update_set(path: str, name: str):
@@ -797,7 +798,9 @@ def enable_url_login():
797
798
  Configure the instance to allow login via URL.
798
799
 
799
800
  """
800
- _set_sys_property(property_name="glide.security.restrict.get.login", value="false")
801
+ set_sys_property(
802
+ instance=SNowInstance(), property_name="glide.security.restrict.get.login", value="false"
803
+ )
801
804
  logging.info("URL login enabled.")
802
805
 
803
806
 
@@ -808,7 +811,28 @@ def disable_password_policies():
808
811
  Notes: this is required to allow the creation of users with weak passwords.
809
812
 
810
813
  """
811
- _set_sys_property(property_name="glide.security.password.policy.enabled", value="false")
814
+ set_sys_property(
815
+ instance=SNowInstance(),
816
+ property_name="glide.security.password.policy.enabled",
817
+ value="false",
818
+ )
819
+ set_sys_property(
820
+ instance=SNowInstance(), property_name="glide.apply.password_policy.on_login", value="false"
821
+ )
822
+ # Exception handling since this property is sometimes read-only on some instances
823
+ try:
824
+ set_sys_property(
825
+ instance=SNowInstance(),
826
+ property_name="glide.authenticate.api.user.reset_password.mandatory",
827
+ value="false",
828
+ )
829
+ except Exception:
830
+ logging.warning(
831
+ "Warning: Failed to set sys property "
832
+ "'glide.authenticate.api.user.reset_password.mandatory'. Continuing.",
833
+ exc_info=True,
834
+ )
835
+
812
836
  logging.info("Password policies disabled.")
813
837
 
814
838
 
@@ -817,8 +841,14 @@ def disable_guided_tours():
817
841
  Hide guided tour popups
818
842
 
819
843
  """
820
- _set_sys_property(property_name="com.snc.guided_tours.sp.enable", value="false")
821
- _set_sys_property(property_name="com.snc.guided_tours.standard_ui.enable", value="false")
844
+ set_sys_property(
845
+ instance=SNowInstance(), property_name="com.snc.guided_tours.sp.enable", value="false"
846
+ )
847
+ set_sys_property(
848
+ instance=SNowInstance(),
849
+ property_name="com.snc.guided_tours.standard_ui.enable",
850
+ value="false",
851
+ )
822
852
  logging.info("Guided tours disabled.")
823
853
 
824
854
 
@@ -836,7 +866,9 @@ def disable_analytics_popups():
836
866
  Disable analytics popups (needs to be done through UI since Vancouver release)
837
867
 
838
868
  """
839
- _set_sys_property(property_name="glide.analytics.enabled", value="false")
869
+ set_sys_property(
870
+ instance=SNowInstance(), property_name="glide.analytics.enabled", value="false"
871
+ )
840
872
  logging.info("Analytics popups disabled.")
841
873
 
842
874
 
@@ -850,7 +882,8 @@ def setup_ui_themes():
850
882
  check_ui_themes_installed()
851
883
 
852
884
  logging.info("Setting default UI theme")
853
- _set_sys_property(
885
+ set_sys_property(
886
+ instance=SNowInstance(),
854
887
  property_name="glide.ui.polaris.theme.custom",
855
888
  value=get_workarena_theme_variants(SNowInstance())[0]["theme.sys_id"],
856
889
  )
@@ -894,7 +927,9 @@ def check_ui_themes_installed():
894
927
 
895
928
  def set_home_page():
896
929
  logging.info("Setting default home page")
897
- _set_sys_property(property_name="glide.login.home", value="/now/nav/ui/home")
930
+ set_sys_property(
931
+ instance=SNowInstance(), property_name="glide.login.home", value="/now/nav/ui/home"
932
+ )
898
933
 
899
934
 
900
935
  def wipe_system_admin_preferences():
@@ -918,9 +953,9 @@ def wipe_system_admin_preferences():
918
953
  )
919
954
 
920
955
 
921
- def is_report_filter_using_time(filter):
956
+ def is_report_filter_using_relative_time(filter):
922
957
  """
923
- Heuristic to check if a report is filtering based on time
958
+ Heuristic to check if a report is filtering based on relative time
924
959
 
925
960
  This aims to detect the use of functions like "gs.endOfToday()". To avoid hardcoding all of them,
926
961
  we simply check for the use of keywords. Our filter is definitely too wide, but that's ok.
@@ -938,6 +973,32 @@ def patch_report_filters():
938
973
  logging.info("Patching reports with date filter...")
939
974
 
940
975
  instance = SNowInstance()
976
+ filter_config = instance.report_filter_config
977
+
978
+ # If the report date filter is already set, we use the existing values (would be the case on reinstall)
979
+ if not filter_config:
980
+ # Set the report date filter to current date as YYYY-MM-DD and time filter to current time as HH:MM:SS
981
+ now = datetime.now()
982
+ report_date_filter = now.strftime("%Y-%m-%d")
983
+ report_time_filter = now.strftime("%H:%M:%S")
984
+ # ... save the filter config
985
+ logging.info(
986
+ f"Setting report date filter to {report_date_filter} and time filter to {report_time_filter} via {REPORT_FILTER_PROPERTY}"
987
+ )
988
+ set_sys_property(
989
+ instance=instance,
990
+ property_name=REPORT_FILTER_PROPERTY,
991
+ value=json.dumps(
992
+ {"report_date_filter": report_date_filter, "report_time_filter": report_time_filter}
993
+ ),
994
+ )
995
+ else:
996
+ # Use the existing configuration
997
+ logging.info(
998
+ f"Using existing report date filter {filter_config['report_date_filter']} and time filter {filter_config['report_time_filter']}"
999
+ )
1000
+ report_date_filter = filter_config["report_date_filter"]
1001
+ report_time_filter = filter_config["report_time_filter"]
941
1002
 
942
1003
  # Get all reports that are not already patched
943
1004
  reports = table_api_call(
@@ -959,27 +1020,31 @@ def patch_report_filters():
959
1020
  logging.info(f"Discarding report {report['title']} {report['sys_id']}...")
960
1021
  raise NotImplementedError() # Mark for deletion
961
1022
 
962
- if not is_report_filter_using_time(report["filter"]):
1023
+ if not is_report_filter_using_relative_time(report["filter"]):
963
1024
  # That's a report we want to keep (use date cutoff filter)
964
- filter_date = REPORT_DATE_FILTER
1025
+ filter_date = report_date_filter
1026
+ filter_time = report_time_filter
965
1027
  logging.info(
966
1028
  f"Keeping report {report['title']} {report['sys_id']} (columns: {sys_created_on_cols})..."
967
1029
  )
968
1030
  else:
969
- # XXX: We do not support reports with filters that rely on time (e.g., last 10 days) because
1031
+ # XXX: We do not support reports with filters that rely on relative time (e.g., last 10 days) because
970
1032
  # there are not stable. In this case, we don't delete them but add a filter to make
971
1033
  # them empty. They will be shown as "No data available".
972
1034
  logging.info(
973
1035
  f"Disabling report {report['title']} {report['sys_id']} because it uses time filters..."
974
1036
  )
975
1037
  filter_date = "1900-01-01"
1038
+ filter_time = "00:00:00"
976
1039
 
1040
+ # Format the filter
977
1041
  filter = "".join(
978
1042
  [
979
- f"^{col}<javascript:gs.dateGenerate('{filter_date}','00:00:00')"
1043
+ f"^{col}<javascript:gs.dateGenerate('{filter_date}','{filter_time}')"
980
1044
  for col in sys_created_on_cols
981
1045
  ]
982
1046
  ) + ("^" if len(report["filter"]) > 0 and not report["filter"].startswith("^") else "")
1047
+ # Patch the report with the new filter
983
1048
  table_api_call(
984
1049
  instance=instance,
985
1050
  table=f"sys_report/{report['sys_id']}",
@@ -1055,7 +1120,11 @@ def setup():
1055
1120
 
1056
1121
  # Save installation date
1057
1122
  logging.info("Saving installation date")
1058
- _set_sys_property(property_name="workarena.installation.date", value=datetime.now().isoformat())
1123
+ set_sys_property(
1124
+ instance=SNowInstance(),
1125
+ property_name="workarena.installation.date",
1126
+ value=datetime.now().isoformat(),
1127
+ )
1059
1128
 
1060
1129
  logging.info("WorkArena setup complete.")
1061
1130
 
@@ -1065,10 +1134,29 @@ def main():
1065
1134
  Entrypoint for package CLI installation command
1066
1135
 
1067
1136
  """
1137
+ parser = argparse.ArgumentParser(
1138
+ description="Install WorkArena artifacts on a ServiceNow instance."
1139
+ )
1140
+ parser.add_argument(
1141
+ "--instance-url", required=True, help="URL of the target ServiceNow instance."
1142
+ )
1143
+ parser.add_argument(
1144
+ "--instance-password",
1145
+ required=True,
1146
+ help="Password for the admin user on the target ServiceNow instance.",
1147
+ )
1148
+ args = parser.parse_args()
1149
+
1150
+ global _CLI_INSTANCE_URL, _CLI_INSTANCE_PASSWORD
1151
+ _CLI_INSTANCE_URL = args.instance_url
1152
+ _CLI_INSTANCE_PASSWORD = args.instance_password
1153
+
1068
1154
  logging.basicConfig(level=logging.INFO)
1069
1155
 
1070
1156
  try:
1071
- past_install_date = _get_sys_property("workarena.installation.date")
1157
+ past_install_date = get_sys_property(
1158
+ instance=SNowInstance(), property_name="workarena.installation.date"
1159
+ )
1072
1160
  logging.info(f"Detected previous installation on {past_install_date}. Reinstalling...")
1073
1161
  except:
1074
1162
  past_install_date = "never"
@@ -1,11 +1,88 @@
1
+ import base64
2
+ import json
3
+ import logging
1
4
  import os
5
+ import random
2
6
  import requests
3
- import re
7
+ from itertools import cycle
4
8
 
9
+ from huggingface_hub import hf_hub_download
10
+ from huggingface_hub.utils import disable_progress_bars
5
11
  from playwright.sync_api import sync_playwright
6
12
  from typing import Optional
7
13
 
8
- from .config import SNOW_BROWSER_TIMEOUT
14
+ from .config import (
15
+ INSTANCE_REPO_FILENAME,
16
+ INSTANCE_REPO_ID,
17
+ INSTANCE_REPO_TYPE,
18
+ INSTANCE_XOR_SEED,
19
+ REPORT_FILTER_PROPERTY,
20
+ SNOW_BROWSER_TIMEOUT,
21
+ )
22
+
23
+
24
+ # Required to read the instance credentials
25
+ if not INSTANCE_XOR_SEED:
26
+ raise ValueError("INSTANCE_XOR_SEED must be configured")
27
+
28
+
29
+ def _xor_cipher(data: bytes, key: bytes) -> bytes:
30
+ return bytes(b ^ k for b, k in zip(data, cycle(key)))
31
+
32
+
33
+ def decrypt_instance_password(encrypted_password: str) -> str:
34
+ """Decrypt a base64-encoded XOR-obfuscated password using the shared key."""
35
+
36
+ cipher_bytes = base64.b64decode(encrypted_password)
37
+ plain_bytes = _xor_cipher(cipher_bytes, INSTANCE_XOR_SEED.encode("utf-8"))
38
+ return plain_bytes.decode("utf-8")
39
+
40
+
41
+ def encrypt_instance_password(password: str) -> str:
42
+ """Helper to produce encrypted passwords for populating the instance file."""
43
+
44
+ cipher_bytes = _xor_cipher(password.encode("utf-8"), INSTANCE_XOR_SEED.encode("utf-8"))
45
+ return base64.b64encode(cipher_bytes).decode("utf-8")
46
+
47
+
48
+ def fetch_instances():
49
+ """
50
+ Load the latest instances from either a custom pool (SNOW_INSTANCE_POOL env var) or the gated HF dataset.
51
+ """
52
+ pool_path = os.getenv("SNOW_INSTANCE_POOL")
53
+ if pool_path:
54
+ path = os.path.expanduser(pool_path)
55
+ if not os.path.exists(path):
56
+ raise FileNotFoundError(
57
+ f"SNOW_INSTANCE_POOL points to '{pool_path}', but the file does not exist."
58
+ )
59
+ logging.info("Loading ServiceNow instances from custom pool: %s", path)
60
+ else:
61
+ try:
62
+ disable_progress_bars()
63
+ path = hf_hub_download(
64
+ repo_id=INSTANCE_REPO_ID,
65
+ filename=INSTANCE_REPO_FILENAME,
66
+ repo_type=INSTANCE_REPO_TYPE,
67
+ )
68
+ logging.info("Loaded ServiceNow instances from the default instance pool.")
69
+ except Exception as e:
70
+ raise RuntimeError(
71
+ f"Could not access {INSTANCE_REPO_ID}/{INSTANCE_REPO_FILENAME}. "
72
+ "Make sure you have been granted access to the gated repo and that you are "
73
+ "authenticated (run `huggingface-cli login` or set HUGGING_FACE_HUB_TOKEN)."
74
+ ) from e
75
+
76
+ with open(path, "r", encoding="utf-8") as f:
77
+ entries = json.load(f)
78
+
79
+ for entry in entries:
80
+ entry["url"] = entry["u"]
81
+ entry["password"] = decrypt_instance_password(entry["p"])
82
+ del entry["u"]
83
+ del entry["p"]
84
+
85
+ return entries
9
86
 
10
87
 
11
88
  class SNowInstance:
@@ -25,31 +102,39 @@ class SNowInstance:
25
102
  Parameters:
26
103
  -----------
27
104
  snow_url: str
28
- The URL of a SNow instance. If None, will try to get the value from the environment variable SNOW_INSTANCE_URL.
105
+ The URL of a SNow instance. When omitted, the constructor first looks for SNOW_INSTANCE_URL and falls back
106
+ to a random instance from the benchmark's instance pool if the environment variable is not set.
29
107
  snow_credentials: (str, str)
30
- The username and password used to access the SNow instance. If None, will try to get the values from the
31
- environment variables SNOW_INSTANCE_UNAME and SNOW_INSTANCE_PWD.
108
+ The username and password used to access the SNow instance. When omitted, environment variables
109
+ SNOW_INSTANCE_UNAME/SNOW_INSTANCE_PWD are used if set; otherwise, a random instance from the benchmark's
110
+ instance pool is selected.
32
111
 
33
112
  """
34
113
  # try to get these values from environment variables if not provided
35
- if snow_url is None:
36
- if "SNOW_INSTANCE_URL" in os.environ:
114
+ if snow_url is None or snow_credentials is None:
115
+
116
+ # Check if all required environment variables are set and if yes, fetch url and credentials from there
117
+ if (
118
+ "SNOW_INSTANCE_URL" in os.environ
119
+ and "SNOW_INSTANCE_UNAME" in os.environ
120
+ and "SNOW_INSTANCE_PWD" in os.environ
121
+ ):
37
122
  snow_url = os.environ["SNOW_INSTANCE_URL"]
38
- else:
39
- raise ValueError(
40
- f"Please provide a ServiceNow instance URL (you can use the environment variable SNOW_INSTANCE_URL)"
41
- )
42
-
43
- if snow_credentials is None:
44
- if "SNOW_INSTANCE_UNAME" in os.environ and "SNOW_INSTANCE_PWD" in os.environ:
45
123
  snow_credentials = (
46
124
  os.environ["SNOW_INSTANCE_UNAME"],
47
125
  os.environ["SNOW_INSTANCE_PWD"],
48
126
  )
127
+
128
+ # Otherwise, load all instances and select one randomly
49
129
  else:
50
- raise ValueError(
51
- f"Please provide ServiceNow credentials (you can use the environment variables SNOW_INSTANCE_UNAME and SNOW_INSTANCE_PWD)"
52
- )
130
+ instances = fetch_instances()
131
+ if not instances:
132
+ raise ValueError(
133
+ f"No instances found in the dataset {INSTANCE_REPO_ID}. Please provide instance details via parameters or environment variables."
134
+ )
135
+ instance = random.choice(instances)
136
+ snow_url = instance["url"]
137
+ snow_credentials = ("admin", instance["password"])
53
138
 
54
139
  # remove trailing slashes in the URL, if any
55
140
  self.snow_url = snow_url.rstrip("/")
@@ -124,3 +209,25 @@ class SNowInstance:
124
209
  browser.close()
125
210
 
126
211
  return release_info
212
+
213
+ @property
214
+ def report_filter_config(self) -> dict:
215
+ """
216
+ Get the report filter configuration from the ServiceNow instance.
217
+
218
+ Returns:
219
+ --------
220
+ dict
221
+ The report filter configuration, or an empty dictionary if not found.
222
+
223
+ """
224
+ from .api.system_properties import (
225
+ get_sys_property,
226
+ ) # Import here to avoid circular import issues
227
+
228
+ try:
229
+ config = get_sys_property(self, REPORT_FILTER_PROPERTY)
230
+ config = json.loads(config)
231
+ return config
232
+ except Exception:
233
+ return None
@@ -19,7 +19,6 @@ from ..config import (
19
19
  DASHBOARD_RETRIEVAL_VALUE_CONFIG_PATH,
20
20
  REPORT_RETRIEVAL_MINMAX_CONFIG_PATH,
21
21
  REPORT_RETRIEVAL_VALUE_CONFIG_PATH,
22
- REPORT_DATE_FILTER,
23
22
  REPORT_PATCH_FLAG,
24
23
  )
25
24
  from ..instance import SNowInstance
@@ -295,9 +294,29 @@ class DashboardRetrievalTask(AbstractServiceNowTask, ABC):
295
294
  """,
296
295
  ]
297
296
 
297
+ def _get_filter_config(self) -> str:
298
+ # Get report filter config
299
+ config = self.instance.report_filter_config
300
+ if config is None:
301
+ REPORT_DATE_FILTER = REPORT_TIME_FILTER = None
302
+ else:
303
+ REPORT_DATE_FILTER = config["report_date_filter"]
304
+ REPORT_TIME_FILTER = config["report_time_filter"]
305
+ del config
306
+
307
+ # Check that the report filters are properly setup
308
+ if REPORT_DATE_FILTER is None or REPORT_TIME_FILTER is None:
309
+ raise RuntimeError(
310
+ "The report date and time filters are not set. Please run the install script to set them."
311
+ )
312
+ return REPORT_DATE_FILTER, REPORT_TIME_FILTER
313
+
298
314
  def setup_goal(self, page: playwright.sync_api.Page) -> Tuple[str | dict]:
299
315
  super().setup_goal(page=page)
300
316
 
317
+ # Get the instance report filter config
318
+ REPORT_DATE_FILTER, REPORT_TIME_FILTER = self._get_filter_config()
319
+
301
320
  # Configure task
302
321
  # ... sample a configuration
303
322
  self.config = (
@@ -305,7 +324,9 @@ class DashboardRetrievalTask(AbstractServiceNowTask, ABC):
305
324
  )
306
325
  # ... set start URL based on config
307
326
  # ...... some of the reports have need a date filter to be applied so we do this by patching a placeholder in the URL
308
- self.start_url = self.instance.snow_url + self.config["url"]
327
+ self.start_url = self.instance.snow_url + self.config["url"].replace(
328
+ "REPORT_DATE_FILTER", REPORT_DATE_FILTER
329
+ ).replace("REPORT_TIME_FILTER", REPORT_TIME_FILTER)
309
330
 
310
331
  # Produce goal string based on question type
311
332
  chart_locator = (
@@ -616,6 +637,15 @@ class DashboardRetrievalTask(AbstractServiceNowTask, ABC):
616
637
  The types of questions to sample from (uniformely)
617
638
 
618
639
  """
640
+ # Get the instance report filter config
641
+ REPORT_DATE_FILTER, REPORT_TIME_FILTER = self._get_filter_config()
642
+
643
+ # Check that the report filters are properly setup
644
+ if REPORT_DATE_FILTER is None or REPORT_TIME_FILTER is None:
645
+ raise RuntimeError(
646
+ "The report date and time filters are not set. Please run the install script to set them."
647
+ )
648
+
619
649
  # Generate a bunch of reports based on valid table fields
620
650
  ON_THE_FLY_REPORTS = []
621
651
  for table in [
@@ -678,7 +708,7 @@ class DashboardRetrievalTask(AbstractServiceNowTask, ABC):
678
708
  # On the fly generated report
679
709
  if not report.get("sys_id", None):
680
710
  # ... these receive a filter that is added through the URL
681
- url = f"/now/nav/ui/classic/params/target/sys_report_template.do%3Fsysparm_field%3D{report['field']}%26sysparm_type%3D{report['type']}%26sysparm_table%3D{report['table']}%26sysparm_from_list%3Dtrue%26sysparm_chart_size%3Dlarge%26sysparm_manual_labor%3Dtrue%26sysparm_query=sys_created_on<javascript:gs.dateGenerate('{REPORT_DATE_FILTER}','00:00:00')^EQ"
711
+ url = f"/now/nav/ui/classic/params/target/sys_report_template.do%3Fsysparm_field%3D{report['field']}%26sysparm_type%3D{report['type']}%26sysparm_table%3D{report['table']}%26sysparm_from_list%3Dtrue%26sysparm_chart_size%3Dlarge%26sysparm_manual_labor%3Dtrue%26sysparm_query=sys_created_on<javascript:gs.dateGenerate('{REPORT_DATE_FILTER}','{REPORT_TIME_FILTER}')^EQ"
682
712
  # Report from the database
683
713
  else:
684
714
  url = f"/now/nav/ui/classic/params/target/sys_report_template.do%3Fjvar_report_id={report['sys_id']}"
@@ -776,7 +806,7 @@ class SingleChartMeanMedianModeRetrievalTask(
776
806
 
777
807
 
778
808
  class WorkLoadBalancingMinMaxRetrievalTask(
779
- MultiChartMinMaxRetrievalTask, CompositionalBuildingBlockTask
809
+ SingleChartMinMaxRetrievalTask, CompositionalBuildingBlockTask
780
810
  ):
781
811
  def all_configs(self):
782
812
  return json.load(open(REPORT_RETRIEVAL_MINMAX_CONFIG_PATH, "r"))
@@ -15,7 +15,6 @@ from playwright.sync_api import sync_playwright
15
15
 
16
16
  from browsergym.workarena.api.utils import table_api_call, table_column_info
17
17
  from browsergym.workarena.config import (
18
- REPORT_DATE_FILTER,
19
18
  REPORT_PATCH_FLAG,
20
19
  REPORT_RETRIEVAL_MINMAX_CONFIG_PATH,
21
20
  REPORT_RETRIEVAL_VALUE_CONFIG_PATH,
@@ -44,6 +43,10 @@ class DummyDashboard(DashboardRetrievalTask):
44
43
 
45
44
 
46
45
  def get_report_urls(instance):
46
+ # Get the instance report filter config
47
+ REPORT_DATE_FILTER, REPORT_TIME_FILTER = instance._get_filter_config()
48
+ raise NotImplementedError("TODO: Include the time filter as in dashboard.py")
49
+
47
50
  # Generate a bunch of reports on the fly based on valid table fields
48
51
  ON_THE_FLY_REPORTS = []
49
52
  for table in [
@@ -226,7 +229,13 @@ def get_all_configs_by_url(url, is_report):
226
229
 
227
230
 
228
231
  if __name__ == "__main__":
229
- instance = SNowInstance()
232
+
233
+ # XXX: Make sure to specific the exact instance on which to generate configs (and not use a random one)
234
+ raise NotImplementedError(
235
+ "Make sure to specific instance URL and credentials below, then comment this line."
236
+ )
237
+ instance = SNowInstance(snow_url=None, snow_credentials=None)
238
+
230
239
  reports = get_report_urls(instance)
231
240
  gen_func = partial(get_all_configs_by_url, is_report=REPORT)
232
241
 
@@ -6,7 +6,10 @@ NUM_CONFIGS = 650 # number of impersonation tasks in the paper
6
6
 
7
7
 
8
8
  def get_all_impersonation_users():
9
- instance = SNowInstance()
9
+ raise NotImplementedError(
10
+ "Make sure to specific instance URL and credentials below, then comment this line."
11
+ )
12
+ instance = SNowInstance(snow_url=None, snow_credentials=None)
10
13
  candidate_users = [
11
14
  u["first_name"] + " " + u["last_name"]
12
15
  for u in table_api_call(
@@ -1,3 +1,4 @@
1
+ # TODO: Can we delete this file?
1
2
  import json
2
3
  import random
3
4
 
@@ -9,9 +10,8 @@ from tenacity import retry, stop_after_attempt
9
10
  from tqdm import tqdm
10
11
 
11
12
 
12
- def generate_all_kb_configs(instance=None, num_configs=1000) -> list[dict]:
13
+ def generate_all_kb_configs(instance, num_configs=1000) -> list[dict]:
13
14
  """Generate all possible KB configs"""
14
- instance = instance if instance is not None else SNowInstance()
15
15
  with open(KB_FILEPATH, "r") as f:
16
16
  kb_entries = json.load(f)
17
17
  all_configs = []
@@ -33,5 +33,7 @@ def generate_all_kb_configs(instance=None, num_configs=1000) -> list[dict]:
33
33
 
34
34
 
35
35
  if __name__ == "__main__":
36
-
37
- validate_kb_configs()
36
+ raise NotImplementedError(
37
+ "Make sure to specific instance URL and credentials below, then comment this line."
38
+ )
39
+ generate_all_kb_configs(instance=SNowInstance(snow_url=None))