pipeline-eds 0.2.4__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 (62) hide show
  1. pipeline/__init__.py +4 -0
  2. pipeline/__main__.py +1 -0
  3. pipeline/api/__init__.py +0 -0
  4. pipeline/api/eds.py +980 -0
  5. pipeline/api/rjn.py +157 -0
  6. pipeline/api/status_api.py +9 -0
  7. pipeline/calls.py +108 -0
  8. pipeline/cli.py +282 -0
  9. pipeline/configrationmanager.py +22 -0
  10. pipeline/decorators.py +13 -0
  11. pipeline/env.py +61 -0
  12. pipeline/environment.py +59 -0
  13. pipeline/gui_fastapi_plotly_live.py +78 -0
  14. pipeline/gui_mpl_live.py +113 -0
  15. pipeline/helpers.py +125 -0
  16. pipeline/logging_setup.py +45 -0
  17. pipeline/pastehelpers.py +10 -0
  18. pipeline/philosophy.py +62 -0
  19. pipeline/plotbuffer.py +21 -0
  20. pipeline/points_loader.py +19 -0
  21. pipeline/queriesmanager.py +122 -0
  22. pipeline/time_manager.py +211 -0
  23. pipeline/workspace_manager.py +253 -0
  24. pipeline_eds-0.2.4.dist-info/LICENSE +14 -0
  25. pipeline_eds-0.2.4.dist-info/METADATA +238 -0
  26. pipeline_eds-0.2.4.dist-info/RECORD +62 -0
  27. pipeline_eds-0.2.4.dist-info/WHEEL +4 -0
  28. pipeline_eds-0.2.4.dist-info/entry_points.txt +6 -0
  29. workspaces/default-workspace.toml +3 -0
  30. workspaces/eds_to_rjn/__init__.py +0 -0
  31. workspaces/eds_to_rjn/code/__init__.py +0 -0
  32. workspaces/eds_to_rjn/code/aggregator.py +84 -0
  33. workspaces/eds_to_rjn/code/collector.py +60 -0
  34. workspaces/eds_to_rjn/code/sanitizer.py +40 -0
  35. workspaces/eds_to_rjn/code/storage.py +16 -0
  36. workspaces/eds_to_rjn/configurations/config_time.toml +11 -0
  37. workspaces/eds_to_rjn/configurations/configuration.toml +2 -0
  38. workspaces/eds_to_rjn/exports/README.md +7 -0
  39. workspaces/eds_to_rjn/exports/aggregate/README.md +7 -0
  40. workspaces/eds_to_rjn/exports/aggregate/live_data - Copy.csv +355 -0
  41. workspaces/eds_to_rjn/exports/aggregate/live_data_EFF.csv +17521 -0
  42. workspaces/eds_to_rjn/exports/aggregate/live_data_INF.csv +17521 -0
  43. workspaces/eds_to_rjn/exports/export_eds_points_neo.txt +11015 -0
  44. workspaces/eds_to_rjn/exports/manual_data_load_to_postman_wetwell.csv +8759 -0
  45. workspaces/eds_to_rjn/exports/manual_data_load_to_postman_wetwell.xlsx +0 -0
  46. workspaces/eds_to_rjn/exports/manual_effluent.csv +8759 -0
  47. workspaces/eds_to_rjn/exports/manual_influent.csv +8759 -0
  48. workspaces/eds_to_rjn/exports/manual_wetwell.csv +8761 -0
  49. workspaces/eds_to_rjn/history/time_sample.txt +0 -0
  50. workspaces/eds_to_rjn/imports/zdMaxson_idcsD321E_sid11003.toml +14 -0
  51. workspaces/eds_to_rjn/imports/zdMaxson_idcsFI8001_sid8528.toml +14 -0
  52. workspaces/eds_to_rjn/imports/zdMaxson_idcsM100FI_sid2308.toml +14 -0
  53. workspaces/eds_to_rjn/imports/zdMaxson_idcsM310LI_sid2382.toml +14 -0
  54. workspaces/eds_to_rjn/queries/default-queries.toml +4 -0
  55. workspaces/eds_to_rjn/queries/points-maxson.csv +4 -0
  56. workspaces/eds_to_rjn/queries/points-stiles.csv +4 -0
  57. workspaces/eds_to_rjn/queries/timestamps_success.json +20 -0
  58. workspaces/eds_to_rjn/scripts/__init__.py +0 -0
  59. workspaces/eds_to_rjn/scripts/daemon_runner.py +212 -0
  60. workspaces/eds_to_rjn/secrets/README.md +24 -0
  61. workspaces/eds_to_rjn/secrets/secrets-example.yaml +15 -0
  62. workspaces/eds_to_termux/..txt +0 -0
@@ -0,0 +1,211 @@
1
+ from datetime import datetime, timezone
2
+ from typing import Union
3
+ import click
4
+ try:
5
+ from zoneinfo import ZoneInfo
6
+ except ImportError:
7
+ from backports.zoneinfo import ZoneInfo
8
+
9
+
10
+ class TimeManager:
11
+ """
12
+ TimeManager is a flexible utility for handling various datetime representations.
13
+
14
+ Supports initialization from:
15
+ - ISO 8601 string (e.g., "2025-07-19T15:00:00Z")
16
+ - Formatted datetime string (e.g., "2025-07-19 15:00:00")
17
+ - Unix timestamp as int or float
18
+ - datetime.datetime object
19
+
20
+ Example usage:
21
+ tm1 = TimeManager("2025-07-19T15:00:00Z") # ISO 8601
22
+ tm2 = TimeManager("2025-07-19 15:00:00") # formatted string
23
+ tm3 = TimeManager(1752946800) # unix int
24
+ tm4 = TimeManager(1752946800.0) # unix float
25
+ tm5 = TimeManager(datetime(2025, 7, 19, 15, 0)) # datetime object
26
+
27
+ print(tm1.as_formatted_date_time()) # → '2025-07-19 15:00:00'
28
+ print(tm1.as_formatted_time()) # → '15:00:00'
29
+ print(tm1.as_isoz()) # → '2025-07-19T15:00:00Z'
30
+ print(tm1.as_unix()) # → 1752946800
31
+ print(tm1.as_datetime()) # → datetime.datetime(2025, 7, 19, 15, 0)
32
+
33
+ rounded_tm = tm1.round_down_to_nearest_five()
34
+ print(rounded_tm.as_formatted_date_time())
35
+
36
+ now_tm = TimeManager.now()
37
+ now_rounded_tm = TimeManager.now_rounded_to_five()
38
+ """
39
+
40
+ HOW_TO_UTCZ_DOC = """
41
+ # HOW TO CONVERT TIME BEFORE USING TIMEMANAGER
42
+ ## 1. Create a datetime in Central Time
43
+ central_time = datetime(2025, 7, 19, 10, 0, tzinfo=ZoneInfo("America/Chicago"))
44
+
45
+ ## 2. Convert to UTC
46
+ utc_time = central_time.astimezone(ZoneInfo("UTC"))
47
+
48
+ ## 3. Use the TimeManager class to ensure ISO format (with Z).
49
+ utc_time_z = TimeManager(utc_time).as_isoz()
50
+
51
+ print("Central:", central_time)
52
+ print("UTC: ", utc_time)
53
+
54
+ # ALTERNATIVE METHODS
55
+ ## - Prepare single timestamp (top of the hour UTC)
56
+ ```
57
+ import datetime
58
+ timestamp = datetime.datetime.now(datetime.timezone.utc).replace(minute=0, second=0, microsecond=0)
59
+ timestamp_str = timestamp.strftime('%Y-%m-%d %H:%M:%S')
60
+ ```
61
+
62
+ """
63
+
64
+ def __init__(self, timestamp: Union[str, int, float, datetime]):
65
+ if isinstance(timestamp, datetime):
66
+ self._dt = timestamp.replace(tzinfo=timezone.utc)
67
+ elif isinstance(timestamp, (int, float)):
68
+ self._dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
69
+ '''
70
+ elif isinstance(timestamp, str):
71
+ try:
72
+ # Use fromisoformat (Python 3.7+)
73
+ self._dt = datetime.fromisoformat(timestamp).replace(tzinfo=timezone.utc)
74
+ except ValueError:
75
+ self._dt = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
76
+ '''
77
+ elif isinstance(timestamp, str):
78
+ try:
79
+ if timestamp.endswith("Z"):
80
+ # Strip 'Z' and parse as UTC
81
+ self._dt = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
82
+ else:
83
+ # Try ISO 8601 string with offset (e.g., +00:00)
84
+ self._dt = datetime.fromisoformat(timestamp).astimezone(timezone.utc)
85
+ except ValueError:
86
+ # Fallback to "YYYY-MM-DD HH:MM:SS"
87
+ self._dt = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
88
+
89
+ '''
90
+ elif isinstance(timestamp, str):
91
+ try:
92
+ # Try ISO 8601 with 'Z'
93
+ self._dt = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
94
+ except ValueError:
95
+ # Try formatted string without timezone
96
+ self._dt = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
97
+ '''
98
+ else:
99
+ raise TypeError(f"Unsupported timestamp type: {type(timestamp)}")
100
+
101
+ def as_datetime(self) -> datetime:
102
+ """Return the internal datetime object (UTC)."""
103
+ return self._dt
104
+
105
+ def as_safe_isoformat_for_filename(self) -> str:
106
+ """
107
+ Returns an ISO 8601 formatted UTC time string safe for use in filenames.
108
+ Example: '2025-07-19T23-35-00Z'
109
+ """
110
+ return self.as_datetime().isoformat().replace(":", "-") + "Z"
111
+
112
+ def as_unix(self):# -> int:
113
+ """Return the Unix timestamp as an integer."""
114
+ return int(self._dt.timestamp())
115
+
116
+ def as_isoz(self):# -> str:
117
+ """Return ISO 8601 string (UTC) with 'Z' suffix."""
118
+ return self._dt.strftime("%Y-%m-%dT%H:%M:%SZ")
119
+
120
+ def as_iso(self):# -> str:
121
+ """Return ISO 8601, like datetime.fromtimestamp(ts).isoformat()."""
122
+ return self._dt.isoformat()
123
+
124
+ def as_formatted_date_time(self):# -> str:
125
+ """Return formatted string 'YYYY-MM-DD HH:MM:SS'."""
126
+ return self._dt.strftime("%Y-%m-%d %H:%M:%S")
127
+
128
+ def as_formatted_time(self):# -> str:
129
+ """Return formatted string 'HH:MM:SS'."""
130
+ return self._dt.strftime("%H:%M:%S")
131
+
132
+ def as_excel(self):# -> float:
133
+ """Returns Excel serial number for Windows (based on 1899-12-30 epoch)."""
134
+ unix_ts = self.as_unix()
135
+ return unix_ts / 86400 + 25569 # 86400 seconds in a day
136
+
137
+ def round_down_to_nearest_five(self):# -> "TimeManager":
138
+ """Return new TimeManager rounded down to nearest 5-minute mark."""
139
+ minute = (self._dt.minute // 5) * 5
140
+ rounded_dt = self._dt.replace(minute=minute, second=0, microsecond=0)
141
+ return TimeManager(rounded_dt).as_unix()
142
+
143
+ @staticmethod
144
+ def now():# -> "TimeManager":
145
+ """Return current UTC time as a TimeManager."""
146
+ return TimeManager(datetime.now(timezone.utc)).as_unix()
147
+
148
+
149
+ @staticmethod
150
+ #def from_local(dt: datetime, zone_name: str) -> "TimeManager":
151
+ def from_local(dt, zone_name):
152
+ """
153
+ Convert a local datetime in the given time zone to UTC and return a TimeManager instance.
154
+
155
+ Args:
156
+ dt (datetime): The local datetime (can be naive or aware).
157
+ zone_name (str): A valid IANA time zone string, e.g. 'America/Chicago'.
158
+
159
+ Returns:
160
+ TimeManager: A new instance based on the UTC version of the datetime.
161
+ """
162
+ if dt.tzinfo is None:
163
+ local_dt = dt.replace(tzinfo=ZoneInfo(zone_name))
164
+ else:
165
+ local_dt = dt.astimezone(ZoneInfo(zone_name))
166
+ utc_dt = local_dt.astimezone(timezone.utc)
167
+ return TimeManager(utc_dt)
168
+
169
+
170
+ @staticmethod
171
+ def now_rounded_to_five():# -> "TimeManager":
172
+ """Return current UTC time rounded down to nearest 5 minutes."""
173
+ now = datetime.now(timezone.utc)
174
+ minute = (now.minute // 5) * 5
175
+ rounded = now.replace(minute=minute, second=0, microsecond=0)
176
+ return TimeManager(rounded).as_unix()
177
+
178
+ def __repr__(self):
179
+ return f"TimeManager({self.as_isoz()})"
180
+
181
+ def __str__(self):
182
+ return self.as_formatted_date_time()
183
+
184
+ @click.command()
185
+ def main():
186
+ click.echo("WELCOME TO THE `TimeManager` CLASS")
187
+ click.echo("pipx install mulch")
188
+ click.echo("from mulch.time_manager import TimeManager")
189
+ click.echo("")
190
+ click.echo("test>> click.MultiCommand(True)")
191
+ click.MultiCommand(True)
192
+ click.echo("test>> click.MultiCommand()")
193
+ click.MultiCommand()
194
+
195
+ msg0 = ''' python time_manager.py # runs 'main' by default if you set it as default command (else error)
196
+ python time_manager.py main # run main
197
+ python time_manager.py easteregg # run easteregg
198
+ python time_manager.py howto-utcz # show how-to UTCZ doc
199
+ python time_manager.py license # show license
200
+ '''
201
+ click.echo(msg0)
202
+ def easteregg():
203
+ click.echo("from mulch.philosophy import Philosophy")
204
+ click.echo("PS C:Users/user/dev/pipeline >> poetry run python -m mulch.philosphy --help # click ")
205
+
206
+ def howto_utcz():
207
+ click.echo(TimeManager.HOW_TO_UTCZ_DOC)
208
+ def license():
209
+ pass
210
+ if __name__ == "__main__":
211
+ main()
@@ -0,0 +1,253 @@
1
+ import os
2
+ import toml
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ '''
7
+ Goal:
8
+ Implement default-workspace.toml variable: use-most-recently-edited-workspace-directory
9
+ '''
10
+
11
+ # Configure logging (adjust level as needed)
12
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
13
+
14
+ class WorkspaceManager:
15
+ # It has been chosen to not make the WorkspaceManager a singleton if there is to be batch processing.
16
+
17
+ WORKSPACES_DIR_NAME = 'workspaces'
18
+ QUERIES_DIR_NAME = 'queries'
19
+ IMPORTS_DIR_NAME = 'imports'
20
+ EXPORTS_DIR_NAME = 'exports'
21
+ SCRIPTS_DIR_NAME = 'scripts'
22
+ CONFIGURATIONS_DIR_NAME = 'configurations'
23
+ SECRETS_DIR_NAME ='secrets'
24
+ LOGS_DIR_NAME = 'logs'
25
+ CONFIGURATION_FILE_NAME = 'configuration.toml'
26
+ SECRETS_YAML_FILE_NAME ='secrets.yaml'
27
+ SECRETS_EXAMPLE_YAML_FILE_NAME ='secrets-example.yaml'
28
+ DEFAULT_WORKSPACE_TOML_FILE_NAME = 'default-workspace.toml'
29
+ TIMESTAMPS_JSON_FILE_NAME = 'timestamps_success.json'
30
+ ROOT_DIR = Path(__file__).resolve().parents[2] # root directory
31
+
32
+
33
+ # This climbs out of /src/pipeline/ to find the root.
34
+ # parents[0] → The directory that contains the (this) Python file.
35
+ # parents[1] → The parent of that directory.
36
+ # parents[2] → The grandparent directory (which should be the root), if root_pipeline\src\pipeline\
37
+ # This organization anticipates PyPi packaging.
38
+
39
+
40
+ def __init__(self, workspace_name):
41
+ self.workspace_name = workspace_name
42
+ self.workspaces_dir = self.get_workspaces_dir()
43
+ self.workspace_dir = self.get_workspace_dir()
44
+ self.configurations_dir = self.get_configurations_dir()
45
+ self.exports_dir = self.get_exports_dir()
46
+ self.imports_dir = self.get_imports_dir()
47
+ self.queries_dir = self.get_queries_dir()
48
+ self.secrets_dir = self.get_secrets_dir()
49
+ self.scripts_dir = self.get_scripts_dir()
50
+ self.logs_dir = self.get_logs_dir()
51
+ self.aggregate_dir = self.get_aggregate_dir()
52
+
53
+
54
+ self.check_and_create_dirs(list_dirs =
55
+ [self.workspace_dir,
56
+ self.exports_dir,
57
+ self.imports_dir,
58
+ self.secrets_dir,
59
+ self.scripts_dir,
60
+ self.logs_dir,
61
+ self.aggregate_dir])
62
+
63
+ def get_workspaces_dir(self):
64
+ return self.ROOT_DIR / self.WORKSPACES_DIR_NAME
65
+
66
+ def get_workspace_dir(self):
67
+ return self.get_workspaces_dir() / self.workspace_name
68
+
69
+ def get_exports_dir(self):
70
+ return self.workspace_dir / self.EXPORTS_DIR_NAME
71
+
72
+ def get_exports_file_path(self, filename):
73
+ # Return the full path to the export file
74
+ return self.exports_dir / filename
75
+
76
+ def get_aggregate_dir(self):
77
+ # This is for five-minute aggregation data to be stored between hourly bulk passes
78
+ # This should become defunct once the tabular trend data request is functional
79
+ return self.exports_dir / 'aggregate'
80
+
81
+ def get_configurations_dir(self):
82
+ return self.workspace_dir / self.CONFIGURATIONS_DIR_NAME
83
+
84
+ def get_configuration_file_path(self):
85
+ # Return the full path to the config file or create it from the fallback copy if it exists
86
+ file_path = self.get_configurations_dir() / self.CONFIGURATION_FILE_NAME
87
+ return file_path
88
+
89
+ def get_logs_dir(self):
90
+ return self.workspace_dir / self.LOGS_DIR_NAME
91
+
92
+ def get_imports_dir(self):
93
+ return self.workspace_dir / self.IMPORTS_DIR_NAME
94
+
95
+ def get_imports_file_path(self, filename):
96
+ # Return the full path to the export file
97
+ return self.imports_dir / filename
98
+
99
+ def get_secrets_dir(self):
100
+ return self.workspace_dir / self.SECRETS_DIR_NAME
101
+
102
+ def get_secrets_file_path(self):
103
+ # Return the full path to the config file
104
+ file_path = self.secrets_dir / self.SECRETS_YAML_FILE_NAME
105
+ if not file_path.exists():
106
+ logging.warning(f"Secrets sonfiguration file {self.SECRETS_YAML_FILE_NAME} not found in:\n{self.secrets_dir}.\nHint: Copy and edit the {self.SECRETS_YAML_FILE_NAME}.")
107
+ print("\n")
108
+ choice = str(input(f"Auto-copy {self.SECRETS_EXAMPLE_YAML_FILE_NAME} [Y] or sys.exit() [n] ? "))
109
+ if choice.lower().startswith("y"):
110
+ file_path = self.get_secrets_file_path_or_copy()
111
+ else:
112
+ # edge case, expected once per machine, or less, if the user knows to set up a secrets.yaml file.
113
+ import sys
114
+ sys.exit()
115
+ return file_path
116
+
117
+ def get_secrets_file_path_or_copy(self):
118
+ # Return the full path to the config file or create it from the fallback copy if it exists
119
+ file_path = self.secrets_dir / self.SECRETS_YAML_FILE_NAME
120
+ fallback_file_path = self.secrets_dir / self.SECRETS_EXAMPLE_YAML_FILE_NAME
121
+ if not file_path.exists() and fallback_file_path.exists():
122
+ import shutil
123
+ shutil.copy(fallback_file_path, file_path)
124
+ print(f"{self.SECRETS_YAML_FILE_NAME} not found, copied from {self.SECRETS_YAML_FILE_NAME}")
125
+ elif not file_path.exists() and not fallback_file_path.exists():
126
+ raise FileNotFoundError(f"Configuration file {self.SECRETS_YAML_FILE_NAME} nor {self.SECRETS_EXAMPLE_YAML_FILE_NAME} not found in directory '{self.secrets_dir}'.")
127
+ return file_path
128
+
129
+ def get_scripts_dir(self):
130
+ return self.workspace_dir / self.SCRIPTS_DIR_NAME
131
+
132
+ def get_scripts_file_path(self, filename):
133
+ # Return the full path to the config file
134
+ return self.get_scripts_dir() / filename
135
+
136
+ def get_queries_dir(self):
137
+ return self.workspace_dir / self.QUERIES_DIR_NAME
138
+
139
+ def get_queries_file_path(self,filename): #
140
+ # Return the full path to the config file
141
+ filepath = self.get_queries_dir() / filename
142
+ if not filepath.exists():
143
+ raise FileNotFoundError(f"Query filepath={filepath} not found. \nPossible reason: You are in the wrong project directory.")
144
+ return filepath
145
+
146
+ def get_timestamp_success_file_path(self):
147
+ # Return the full path to the timestamp file
148
+ filepath = self.get_queries_dir() / self.TIMESTAMPS_JSON_FILE_NAME
149
+ logging.info(f"WorkspaceManager.get_timestamp_success_file_path() = {filepath}")
150
+ return filepath
151
+
152
+ def check_and_create_dirs(self, list_dirs):
153
+ for dir_path in list_dirs:
154
+ if not dir_path.exists():
155
+ dir_path.mkdir(parents=True, exist_ok=True)
156
+
157
+ @classmethod
158
+ def get_cwd(cls) -> Path:
159
+ """Return current workspace directory, not the source code root, as a Path instance."""
160
+ # Quick and dirty, not representative of the complex truth or opportunity.
161
+ #cls.ROOT_DIR / 'workspaces' / cls.identify_default_workspace_name()
162
+
163
+ # Pre-Exisiting function, generated some time before July 24, 2025. May as well use that instead. It is good to use your own library. Benefit from having built it.
164
+ return cls.identify_default_workspace_name()
165
+
166
+ @classmethod
167
+ def get_all_workspaces_names(cls):
168
+ """
169
+ Return a list of all workspace names found in the workspaces directory.
170
+ """
171
+ workspaces_dir = cls.ROOT_DIR / cls.WORKSPACES_DIR_NAME
172
+ if not workspaces_dir.exists():
173
+ raise FileNotFoundError(f"Workspaces directory not found at: {workspaces_dir}")
174
+
175
+ workspace_dirs = [
176
+ p.name for p in workspaces_dir.iterdir()
177
+ if p.is_dir() and not p.name.startswith('.') # skip hidden/system folders
178
+ ]
179
+ return workspace_dirs
180
+
181
+ @classmethod
182
+ def identify_default_workspace_path(cls):
183
+ """
184
+ Class method that reads default-workspace.toml to identify the default-workspace path.
185
+ """
186
+ workspace_name = cls.identify_default_workspace_name()
187
+ workspaces_dir = cls.ROOT_DIR / cls.WORKSPACES_DIR_NAME
188
+ default_workspace_path = workspaces_dir / workspace_name
189
+ if not default_workspace_path.exists():
190
+ raise FileNotFoundError(f"Default workspace directory not found: {default_workspace_path}")
191
+ return default_workspace_path
192
+ @classmethod
193
+ def identify_default_workspace_name(cls):
194
+ """
195
+ Class method that reads default-workspace.toml to identify the default-workspace.
196
+ """
197
+
198
+ workspaces_dir = cls.ROOT_DIR / cls.WORKSPACES_DIR_NAME
199
+ logging.info(f"workspaces_dir = {workspaces_dir}\n")
200
+ default_toml_path = workspaces_dir / cls.DEFAULT_WORKSPACE_TOML_FILE_NAME
201
+
202
+ if not default_toml_path.exists():
203
+ raise FileNotFoundError(f"Missing {cls.DEFAULT_WORKSPACE_TOML_FILE_NAME} in {workspaces_dir}")
204
+
205
+ with open(default_toml_path, 'r') as f:
206
+ data = toml.load(f)
207
+ logging.debug(f"data = {data}")
208
+ try:
209
+ return data['default-workspace']['workspace'] # This dictates the proper formatting of the TOML file.
210
+ except KeyError as e:
211
+ raise KeyError(f"Missing key in {cls.DEFAULT_WORKSPACE_TOML_FILE_NAME}: {e}")
212
+
213
+ def get_default_query_file_paths_list(self):
214
+
215
+ default_query_path = self.get_queries_dir()/ 'default-queries.toml'
216
+
217
+ with open(default_query_path, 'r') as f:
218
+ query_config = toml.load(f)
219
+ filenames = query_config['default-query']['files']
220
+ if not isinstance(filenames, list):
221
+ raise ValueError("Expected a list under ['default-query']['files'] in default-queries.toml")
222
+ paths = [self.get_queries_file_path(fname) for fname in filenames]
223
+
224
+ for path in paths:
225
+ if not os.path.exists(path):
226
+ raise FileNotFoundError(f"Query file not found: {path}")
227
+ return paths
228
+
229
+ @property
230
+ def name(self):
231
+ return self.workspace_name
232
+
233
+ def establish_default_workspace():
234
+ workspace_name = WorkspaceManager.identify_default_workspace_name()
235
+ logging.info(f"workspace_name = {workspace_name}")
236
+ workspace_manager = WorkspaceManager(workspace_name)
237
+ logging.info(f"WorkspaceManager.get_workspace_dir() = {WorkspaceManager.get_workspace_dir()}")
238
+ return
239
+
240
+ def demo_establish_default_workspace():
241
+ establish_default_workspace()
242
+
243
+ if __name__ == "__main__":
244
+ import sys
245
+ cmd = sys.argv[1] if len(sys.argv) > 1 else "default"
246
+
247
+ if cmd == "demo-default":
248
+ demo_establish_default_workspace()
249
+ else:
250
+ print("Usage options: \n"
251
+ "poetry run python -m pipeline.api.eds demo-default \n")
252
+
253
+
@@ -0,0 +1,14 @@
1
+ BSD-3 License
2
+
3
+ Copyright (c) 2025 George Clayton Bennett
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+