pipeline-eds 0.2.13__py3-none-any.whl → 0.2.15__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.
- pipeline/api/eds.py +52 -6
- pipeline/cli.py +38 -19
- pipeline/environment.py +2 -0
- pipeline/gui_fastapi_plotly_live.py +31 -5
- pipeline/gui_mpl_live.py +41 -27
- pipeline/gui_plotly_static.py +43 -0
- pipeline/workspace_manager.py +57 -11
- {pipeline_eds-0.2.13.dist-info → pipeline_eds-0.2.15.dist-info}/METADATA +3 -2
- {pipeline_eds-0.2.13.dist-info → pipeline_eds-0.2.15.dist-info}/RECORD +12 -11
- {pipeline_eds-0.2.13.dist-info → pipeline_eds-0.2.15.dist-info}/LICENSE +0 -0
- {pipeline_eds-0.2.13.dist-info → pipeline_eds-0.2.15.dist-info}/WHEEL +0 -0
- {pipeline_eds-0.2.13.dist-info → pipeline_eds-0.2.15.dist-info}/entry_points.txt +0 -0
pipeline/api/eds.py
CHANGED
@@ -165,7 +165,7 @@ class EdsClient:
|
|
165
165
|
|
166
166
|
@staticmethod
|
167
167
|
#def create_tabular_request(session: object, api_url: str, starttime: int, endtime: int, points: list):
|
168
|
-
def
|
168
|
+
def create_tabular_request_(session, api_url, starttime, endtime, points, step_seconds = 300):
|
169
169
|
|
170
170
|
data = {
|
171
171
|
'period': {
|
@@ -189,6 +189,51 @@ class EdsClient:
|
|
189
189
|
response = session.post(f"{api_url}/trend/tabular", json=data, verify=False)
|
190
190
|
#print(f"response = {response}")
|
191
191
|
|
192
|
+
@staticmethod
|
193
|
+
def create_tabular_request(session, api_url, starttime, endtime, points, step_seconds=300):
|
194
|
+
"""
|
195
|
+
Submit a tabular trend request. Returns request id on success, or None if failed.
|
196
|
+
"""
|
197
|
+
|
198
|
+
data = {
|
199
|
+
"period": {
|
200
|
+
"from": starttime,
|
201
|
+
"till": endtime,
|
202
|
+
},
|
203
|
+
"step": step_seconds,
|
204
|
+
"items": [
|
205
|
+
{
|
206
|
+
"pointId": {"iess": p},
|
207
|
+
"shadePriority": "DEFAULT",
|
208
|
+
"function": "AVG",
|
209
|
+
}
|
210
|
+
for p in points
|
211
|
+
],
|
212
|
+
}
|
213
|
+
|
214
|
+
try:
|
215
|
+
res = session.post(f"{api_url}/trend/tabular", json=data, verify=False)
|
216
|
+
except Exception as e:
|
217
|
+
logger.error(f"Request failed to {api_url}/trend/tabular: {e}")
|
218
|
+
return None
|
219
|
+
|
220
|
+
if res.status_code != 200:
|
221
|
+
logger.error(f"Bad status {res.status_code} from server: {res.text}")
|
222
|
+
return None
|
223
|
+
|
224
|
+
try:
|
225
|
+
payload = res.json()
|
226
|
+
except Exception:
|
227
|
+
logger.error(f"Non-JSON response: {res.text}")
|
228
|
+
return None
|
229
|
+
|
230
|
+
req_id = payload.get("id")
|
231
|
+
if not req_id:
|
232
|
+
logger.error(f"No request id in response: {payload}")
|
233
|
+
return None
|
234
|
+
|
235
|
+
return req_id
|
236
|
+
|
192
237
|
@staticmethod
|
193
238
|
def wait_for_request_execution_session(session, api_url, req_id):
|
194
239
|
st = time.time()
|
@@ -726,7 +771,7 @@ def demo_eds_webplot_point_live():
|
|
726
771
|
gui_fastapi_plotly_live.run_gui(data_buffer)
|
727
772
|
|
728
773
|
@log_function_call(level=logging.DEBUG)
|
729
|
-
def load_historic_data(session, iess_list, starttime, endtime):
|
774
|
+
def load_historic_data(session, iess_list, starttime, endtime, step_seconds):
|
730
775
|
|
731
776
|
|
732
777
|
starttime = TimeManager(starttime).as_unix()
|
@@ -734,11 +779,13 @@ def load_historic_data(session, iess_list, starttime, endtime):
|
|
734
779
|
logger.info(f"starttime = {starttime}")
|
735
780
|
logger.info(f"endtime = {endtime}")
|
736
781
|
|
737
|
-
step_seconds = helpers.nice_step(endtime-starttime)
|
738
782
|
|
739
783
|
point_list = iess_list
|
740
784
|
api_url = str(session.base_url)
|
741
785
|
request_id = EdsClient.create_tabular_request(session, api_url, starttime, endtime, points=point_list, step_seconds=step_seconds)
|
786
|
+
if not request_id:
|
787
|
+
logger.warning(f"Could not create tabular request for points: {point_list}")
|
788
|
+
return [] # or None, depending on how you want the CLI to behave
|
742
789
|
EdsClient.wait_for_request_execution_session(session, api_url, request_id)
|
743
790
|
results = EdsClient.get_tabular_trend(session, request_id, point_list)
|
744
791
|
logger.debug(f"len(results) = {len(results)}")
|
@@ -951,10 +998,9 @@ if __name__ == "__main__":
|
|
951
998
|
demo_eds_save_graphics_export()
|
952
999
|
elif cmd == "license":
|
953
1000
|
demo_eds_print_license()
|
954
|
-
elif cmd == "access-workspace"
|
1001
|
+
elif cmd == "access-workspace":
|
955
1002
|
if platform.system().lower() == "windows":
|
956
|
-
#
|
957
|
-
#command = ["Open-FileBrowser", WorkspaceManager.get_cwd()]
|
1003
|
+
# at this level it is correct but the get_cwd() command only knows the default workspace.
|
958
1004
|
command = ["explorer", str(WorkspaceManager.get_cwd())]
|
959
1005
|
subprocess.call(command)
|
960
1006
|
else:
|
pipeline/cli.py
CHANGED
@@ -20,22 +20,23 @@ from pipeline.env import SecretConfig
|
|
20
20
|
from pipeline.workspace_manager import WorkspaceManager
|
21
21
|
|
22
22
|
### Versioning
|
23
|
+
CLI_APP_NAME = "pipeline"
|
23
24
|
def print_version(value: bool):
|
24
25
|
if value:
|
25
26
|
try:
|
26
|
-
typer.secho(f"
|
27
|
+
typer.secho(f"{CLI_APP_NAME} {PIPELINE_VERSION}",fg=typer.colors.GREEN, bold=True)
|
27
28
|
except PackageNotFoundError:
|
28
29
|
typer.echo("Version info not found")
|
29
30
|
raise typer.Exit()
|
30
31
|
try:
|
31
|
-
|
32
|
-
__version__ = version(
|
32
|
+
PIPELINE_VERSION = version(CLI_APP_NAME)
|
33
|
+
__version__ = version(CLI_APP_NAME)
|
33
34
|
except PackageNotFoundError:
|
34
|
-
|
35
|
+
PIPELINE_VERSION = "unknown"
|
35
36
|
|
36
37
|
try:
|
37
38
|
from importlib.metadata import version
|
38
|
-
__version__ = version(
|
39
|
+
__version__ = version(CLI_APP_NAME)
|
39
40
|
except PackageNotFoundError:
|
40
41
|
# fallback if running from source
|
41
42
|
try:
|
@@ -102,7 +103,9 @@ def trend(
|
|
102
103
|
starttime: str = typer.Option(None, "--start", "-s", help="Index from 'mulch order' to choose scaffold source."),
|
103
104
|
endtime: str = typer.Option(None, "--end", "-end", help="Reference a known template for workspace organization."),
|
104
105
|
zd: str = typer.Option('Maxson', "--zd", "-z", help = "Define the EDS ZD from your secrets file. This must correlate with your idcs point selection(s)."),
|
105
|
-
|
106
|
+
workspacename: str = typer.Option(None,"--workspace","-w", help = "Provide the name of the workspace you want to use, for the secrets.yaml credentials and for the timezone config. If a start time is not provided, the workspace queries can checked for the most recent successful timestamp. "),
|
107
|
+
print_csv: bool = typer.Option(False,"--print-csv","-p",help = "Print the CSV style for pasting into Excel."),
|
108
|
+
step_seconds: int = typer.Option(None, "--step-seconds", help="You can explicitly provide the delta between datapoints. If not, ~400 data points will be used, based on the nice_step() function.")
|
106
109
|
):
|
107
110
|
"""
|
108
111
|
Show a curve for a sensor over time.
|
@@ -112,16 +115,16 @@ def trend(
|
|
112
115
|
from pipeline.api.eds import EdsClient, load_historic_data
|
113
116
|
from pipeline import helpers
|
114
117
|
from pipeline.plotbuffer import PlotBuffer
|
115
|
-
from pipeline import gui_fastapi_plotly_live
|
116
118
|
from pipeline import environment
|
117
119
|
from pipeline.workspace_manager import WorkspaceManager
|
118
|
-
|
120
|
+
workspaces_dir = WorkspaceManager.ensure_appdata_workspaces_dir()
|
119
121
|
|
120
122
|
# must set up %appdata for pip/x installation. Use mulch or yeoman for this. And have a secrets filler.
|
121
|
-
if
|
122
|
-
WorkspaceManager.identify_default_workspace_name()
|
123
|
-
wm = WorkspaceManager(
|
124
|
-
|
123
|
+
if workspacename is None:
|
124
|
+
workspacename = WorkspaceManager.identify_default_workspace_name()
|
125
|
+
wm = WorkspaceManager(workspacename)
|
126
|
+
secrets_file_path = wm.get_secrets_file_path()
|
127
|
+
secrets_dict = SecretConfig.load_config(secrets_file_path)
|
125
128
|
|
126
129
|
if zd.lower() == "stiles":
|
127
130
|
zd = "WWTF"
|
@@ -156,22 +159,38 @@ def trend(
|
|
156
159
|
dt_finish = pendulum.parse(endtime, strict=False)
|
157
160
|
|
158
161
|
# Should automatically choose time step granularity based on time length; map
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
+
if step_seconds is None:
|
163
|
+
step_seconds = helpers.nice_step(endtime-starttime)
|
164
|
+
results = load_historic_data(session, iess_list, dt_start, dt_finish, step_seconds)
|
165
|
+
if not results:
|
166
|
+
return
|
167
|
+
|
162
168
|
data_buffer = PlotBuffer()
|
163
169
|
for idx, rows in enumerate(results):
|
164
170
|
for row in rows:
|
165
|
-
label = f"
|
171
|
+
#label = f"({row.get('units')})"
|
172
|
+
label = iess_list[0]
|
166
173
|
ts = helpers.iso(row.get("ts"))
|
167
174
|
av = row.get("value")
|
175
|
+
#print(f"{round(av,2)}")
|
168
176
|
data_buffer.append(label, ts, av) # needs to be adapted for multiple iess sensor results
|
169
|
-
|
177
|
+
#print(f"data_buffer = {data_buffer}")
|
178
|
+
#print(f"data_buffer.get_all() = {data_buffer.get_all()}")
|
170
179
|
if not environment.matplotlib_enabled():
|
171
|
-
|
180
|
+
from pipeline import gui_plotly_static
|
181
|
+
#gui_fastapi_plotly_live.run_gui(data_buffer)
|
182
|
+
gui_plotly_static.show_static(data_buffer)
|
172
183
|
else:
|
173
184
|
from pipeline import gui_mpl_live
|
174
|
-
gui_mpl_live.run_gui(data_buffer)
|
185
|
+
#gui_mpl_live.run_gui(data_buffer)
|
186
|
+
gui_mpl_live.show_static(data_buffer)
|
187
|
+
|
188
|
+
if print_csv:
|
189
|
+
print(f"Time,\\{iess_list[0]}\\,")
|
190
|
+
for idx, rows in enumerate(results):
|
191
|
+
for row in rows:
|
192
|
+
print(f"{helpers.iso(row.get('ts'))},{row.get('value')},")
|
193
|
+
|
175
194
|
|
176
195
|
@app.command()
|
177
196
|
def list_workspaces():
|
pipeline/environment.py
CHANGED
@@ -12,11 +12,13 @@ def vercel():
|
|
12
12
|
return False # hard code this
|
13
13
|
|
14
14
|
def matplotlib_enabled():
|
15
|
+
#print(f"is_termux() = {is_termux()}")
|
15
16
|
if is_termux():
|
16
17
|
return False
|
17
18
|
else:
|
18
19
|
try:
|
19
20
|
import matplotlib
|
21
|
+
return True
|
20
22
|
except ImportError:
|
21
23
|
return False
|
22
24
|
|
@@ -57,12 +57,32 @@ HTML_TEMPLATE = """
|
|
57
57
|
async def index():
|
58
58
|
return HTML_TEMPLATE
|
59
59
|
|
60
|
+
|
60
61
|
@app.get("/data", response_class=JSONResponse)
|
61
62
|
async def get_data():
|
63
|
+
if plot_buffer is None:
|
64
|
+
print("plot_buffer is None")
|
65
|
+
return {}
|
62
66
|
with buffer_lock:
|
63
|
-
data = plot_buffer.get_all()
|
64
|
-
|
65
|
-
|
67
|
+
data = plot_buffer.get_all()
|
68
|
+
print("Data in buffer:", data) # <-- DEBUG
|
69
|
+
fixed_data = {}
|
70
|
+
for label, series in data.items():
|
71
|
+
fixed_data[label] = {
|
72
|
+
"x": [ts + "Z" if not ts.endswith("Z") else ts for ts in series["x"]],
|
73
|
+
"y": series["y"]
|
74
|
+
}
|
75
|
+
return fixed_data
|
76
|
+
"""
|
77
|
+
@app.get("/data", response_class=JSONResponse)
|
78
|
+
async def get_data():
|
79
|
+
return {
|
80
|
+
"Test Series": {
|
81
|
+
"x": ["2025-09-05T15:00:00Z", "2025-09-05T15:05:00Z", "2025-09-05T15:10:00Z"],
|
82
|
+
"y": [1, 3, 2]
|
83
|
+
}
|
84
|
+
}
|
85
|
+
"""
|
66
86
|
def open_browser(port):
|
67
87
|
time.sleep(1) # Give server a moment to start
|
68
88
|
## Open in a new Chrome window (if installed)
|
@@ -71,8 +91,14 @@ def open_browser(port):
|
|
71
91
|
|
72
92
|
webbrowser.open(f"http://127.0.0.1:{port}")
|
73
93
|
|
94
|
+
#def run_gui(buffer, port=8000):
|
95
|
+
# global plot_buffer
|
96
|
+
# plot_buffer = buffer
|
97
|
+
# threading.Thread(target=open_browser, args=(port,), daemon=True).start()
|
98
|
+
# uvicorn.run("src.pipeline.gui_fastapi_plotly_live:app", host="127.0.0.1", port=port, log_level="info", reload=False)
|
99
|
+
|
74
100
|
def run_gui(buffer, port=8000):
|
75
101
|
global plot_buffer
|
76
|
-
plot_buffer = buffer
|
102
|
+
plot_buffer = buffer # set the buffer in this process
|
77
103
|
threading.Thread(target=open_browser, args=(port,), daemon=True).start()
|
78
|
-
uvicorn.run(
|
104
|
+
uvicorn.run(app, host="127.0.0.1", port=port, log_level="info", reload=False) # <- reload=False
|
pipeline/gui_mpl_live.py
CHANGED
@@ -3,6 +3,8 @@ import time
|
|
3
3
|
import logging
|
4
4
|
import matplotlib.pyplot as plt
|
5
5
|
import matplotlib.animation as animation
|
6
|
+
import matplotlib.dates as mdates
|
7
|
+
from datetime import datetime, timedelta
|
6
8
|
from pipeline import helpers
|
7
9
|
from pipeline.plotbuffer import PlotBuffer # Adjust import path as needed
|
8
10
|
from pipeline.time_manager import TimeManager
|
@@ -11,28 +13,6 @@ logger = logging.getLogger(__name__)
|
|
11
13
|
|
12
14
|
PADDING_RATIO = 0.25
|
13
15
|
|
14
|
-
def compute_padded_bounds(data):
|
15
|
-
all_x_vals = []
|
16
|
-
all_y_vals = []
|
17
|
-
|
18
|
-
for series in data.values():
|
19
|
-
all_x_vals.extend(series["x"])
|
20
|
-
all_y_vals.extend(series["y"])
|
21
|
-
|
22
|
-
if not all_x_vals or not all_y_vals:
|
23
|
-
return (0, 1), (0, 1)
|
24
|
-
|
25
|
-
x_min, x_max = min(all_x_vals), max(all_x_vals)
|
26
|
-
y_min, y_max = min(all_y_vals), max(all_y_vals)
|
27
|
-
|
28
|
-
x_pad = max((x_max - x_min) * PADDING_RATIO, 1.0)
|
29
|
-
y_pad = max((y_max - y_min) * PADDING_RATIO, 1.0)
|
30
|
-
|
31
|
-
padded_x = (x_min - x_pad, x_max + x_pad)
|
32
|
-
padded_y = (y_min - y_pad, y_max + y_pad)
|
33
|
-
|
34
|
-
return padded_x, padded_y
|
35
|
-
|
36
16
|
def run_gui(buffer: PlotBuffer, update_interval_ms=1000):
|
37
17
|
"""
|
38
18
|
Runs a matplotlib live updating plot based on the PlotBuffer content.
|
@@ -45,6 +25,11 @@ def run_gui(buffer: PlotBuffer, update_interval_ms=1000):
|
|
45
25
|
ax.set_title("Live Pipeline Data")
|
46
26
|
ax.set_xlabel("Time")
|
47
27
|
ax.set_ylabel("Value")
|
28
|
+
# Auto-locate ticks and auto-format dates
|
29
|
+
locator = mdates.AutoDateLocator()
|
30
|
+
formatter = mdates.AutoDateFormatter(locator)
|
31
|
+
ax.xaxis.set_major_locator(locator)
|
32
|
+
ax.xaxis.set_major_formatter(formatter)
|
48
33
|
|
49
34
|
lines = {}
|
50
35
|
legend_labels = []
|
@@ -80,11 +65,6 @@ def run_gui(buffer: PlotBuffer, update_interval_ms=1000):
|
|
80
65
|
else:
|
81
66
|
lines[label].set_data(x_vals, y_vals)
|
82
67
|
|
83
|
-
# Adjust axes limits with padding
|
84
|
-
padded_x, padded_y = compute_padded_bounds(data)
|
85
|
-
ax.set_xlim(padded_x)
|
86
|
-
ax.set_ylim(padded_y)
|
87
|
-
|
88
68
|
# Format x-axis ticks as human readable time strings
|
89
69
|
|
90
70
|
# Tick positions are x values at those indices
|
@@ -111,3 +91,37 @@ def run_gui(buffer: PlotBuffer, update_interval_ms=1000):
|
|
111
91
|
plt.tight_layout()
|
112
92
|
plt.show()
|
113
93
|
|
94
|
+
def show_static(buffer: PlotBuffer):
|
95
|
+
"""
|
96
|
+
Show a static matplotlib plot of the current PlotBuffer contents,
|
97
|
+
with automatic date formatting based on time span.
|
98
|
+
"""
|
99
|
+
plt.style.use('ggplot')
|
100
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
101
|
+
ax.set_title("Static Pipeline Data")
|
102
|
+
ax.set_xlabel("Time")
|
103
|
+
ax.set_ylabel("Value")
|
104
|
+
|
105
|
+
data = buffer.get_all()
|
106
|
+
if not data:
|
107
|
+
ax.text(0.5, 0.5, "No data to display", ha='center', va='center')
|
108
|
+
plt.show()
|
109
|
+
return
|
110
|
+
|
111
|
+
for label, series in data.items():
|
112
|
+
# Convert strings to datetime objects for better handling
|
113
|
+
x_vals = [TimeManager(ts).as_datetime() for ts in series["x"]]
|
114
|
+
y_vals = series["y"]
|
115
|
+
|
116
|
+
ax.plot(x_vals, y_vals, marker='o', linestyle='-', label=label)
|
117
|
+
|
118
|
+
# Let matplotlib auto-locate ticks and auto-format
|
119
|
+
locator = mdates.AutoDateLocator()
|
120
|
+
formatter = mdates.AutoDateFormatter(locator)
|
121
|
+
ax.xaxis.set_major_locator(locator)
|
122
|
+
ax.xaxis.set_major_formatter(formatter)
|
123
|
+
|
124
|
+
plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
|
125
|
+
ax.legend()
|
126
|
+
plt.tight_layout()
|
127
|
+
plt.show()
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# src/pipeline/gui_plotly_static.py
|
2
|
+
|
3
|
+
import plotly.graph_objs as go
|
4
|
+
import plotly.offline as pyo
|
5
|
+
import webbrowser
|
6
|
+
import tempfile
|
7
|
+
from threading import Lock
|
8
|
+
|
9
|
+
buffer_lock = Lock() # Optional, if you want thread safety
|
10
|
+
|
11
|
+
def show_static(plot_buffer):
|
12
|
+
"""
|
13
|
+
Renders the current contents of plot_buffer as a static HTML plot.
|
14
|
+
Does not listen for updates.
|
15
|
+
"""
|
16
|
+
if plot_buffer is None:
|
17
|
+
print("plot_buffer is None")
|
18
|
+
return
|
19
|
+
|
20
|
+
with buffer_lock:
|
21
|
+
data = plot_buffer.get_all()
|
22
|
+
|
23
|
+
traces = []
|
24
|
+
for label, series in data.items():
|
25
|
+
traces.append(go.Scatter(
|
26
|
+
x=series["x"],
|
27
|
+
y=series["y"],
|
28
|
+
mode="lines+markers",
|
29
|
+
name=label
|
30
|
+
))
|
31
|
+
|
32
|
+
layout = go.Layout(
|
33
|
+
title="EDS Data Plot (Static)",
|
34
|
+
margin=dict(t=40)
|
35
|
+
)
|
36
|
+
|
37
|
+
fig = go.Figure(data=traces, layout=layout)
|
38
|
+
|
39
|
+
# Write to a temporary HTML file
|
40
|
+
tmp_file = tempfile.NamedTemporaryFile(suffix=".html", delete=False)
|
41
|
+
pyo.plot(fig, filename=tmp_file.name, auto_open=False)
|
42
|
+
|
43
|
+
webbrowser.open(f"file://{tmp_file.name}")
|
pipeline/workspace_manager.py
CHANGED
@@ -2,6 +2,7 @@ import os
|
|
2
2
|
import toml
|
3
3
|
import logging
|
4
4
|
from pathlib import Path
|
5
|
+
import sys
|
5
6
|
|
6
7
|
'''
|
7
8
|
Goal:
|
@@ -29,7 +30,14 @@ class WorkspaceManager:
|
|
29
30
|
APP_NAME = "pipeline"
|
30
31
|
|
31
32
|
TIMESTAMPS_JSON_FILE_NAME = 'timestamps_success.json'
|
32
|
-
|
33
|
+
|
34
|
+
# Detect if running in a dev repo vs installed package
|
35
|
+
if getattr(sys, "frozen", False):
|
36
|
+
# Running from a pipx/executable environment
|
37
|
+
ROOT_DIR = None
|
38
|
+
else:
|
39
|
+
# Running from a cloned repo
|
40
|
+
ROOT_DIR = Path(__file__).resolve().parents[2] # root directory
|
33
41
|
|
34
42
|
|
35
43
|
# This climbs out of /src/pipeline/ to find the root.
|
@@ -62,11 +70,38 @@ class WorkspaceManager:
|
|
62
70
|
self.logs_dir,
|
63
71
|
self.aggregate_dir])
|
64
72
|
|
65
|
-
|
66
|
-
|
73
|
+
|
74
|
+
@classmethod
|
75
|
+
def get_workspaces_dir(cls):
|
76
|
+
"""
|
77
|
+
Return workspaces directory depending on environment:
|
78
|
+
- If ROOT_DIR is defined (repo clone), use that
|
79
|
+
- Else use AppData/local platform-specific location
|
80
|
+
"""
|
81
|
+
if cls.ROOT_DIR and (cls.ROOT_DIR / cls.WORKSPACES_DIR_NAME).exists():
|
82
|
+
workspaces_dir = cls.ROOT_DIR / cls.WORKSPACES_DIR_NAME
|
83
|
+
else:
|
84
|
+
workspaces_dir = cls.get_appdata_dir() / cls.WORKSPACES_DIR_NAME
|
85
|
+
workspaces_dir.mkdir(parents=True, exist_ok=True)
|
86
|
+
default_file = workspaces_dir / cls.DEFAULT_WORKSPACE_TOML_FILE_NAME
|
87
|
+
if not default_file.exists():
|
88
|
+
# auto-populate default TOML with most recent workspace
|
89
|
+
recent_ws = cls.most_recent_workspace_name() or "default"
|
90
|
+
default_file.write_text(f"[default-workspace]\nworkspace = '{recent_ws}'\n")
|
91
|
+
return workspaces_dir
|
92
|
+
|
93
|
+
@classmethod
|
94
|
+
def most_recent_workspace_name(cls):
|
95
|
+
workspaces_dir = cls.get_workspaces_dir()
|
96
|
+
all_dirs = [p for p in workspaces_dir.iterdir() if p.is_dir() and not p.name.startswith('.')]
|
97
|
+
if not all_dirs:
|
98
|
+
return None
|
99
|
+
latest = max(all_dirs, key=lambda p: p.stat().st_mtime)
|
100
|
+
return latest.name
|
67
101
|
|
68
102
|
def get_workspace_dir(self):
|
69
|
-
|
103
|
+
# workspace_name is established at instantiation. You want a new name? Initialize a new WorkspaceManager(). It manages one workpspace.
|
104
|
+
return self.get_workspaces_dir() / self.workspace_name
|
70
105
|
|
71
106
|
def get_exports_dir(self):
|
72
107
|
return self.workspace_dir / self.EXPORTS_DIR_NAME
|
@@ -191,13 +226,25 @@ class WorkspaceManager:
|
|
191
226
|
if not default_workspace_path.exists():
|
192
227
|
raise FileNotFoundError(f"Default workspace directory not found: {default_workspace_path}")
|
193
228
|
return default_workspace_path
|
229
|
+
|
230
|
+
'''
|
231
|
+
@classmethod
|
232
|
+
def identify_default_workspace_name(cls, workspaces_dir = None):
|
233
|
+
if workspaces_dir is None:
|
234
|
+
workspaces_dir = cls.get_workspaces_dir()
|
235
|
+
default_file = workspaces_dir / cls.DEFAULT_WORKSPACE_TOML_FILE_NAME
|
236
|
+
if not default_file.exists():
|
237
|
+
default_file.write_text("# Default workspace\n")
|
238
|
+
return str(default_file)
|
239
|
+
'''
|
240
|
+
|
194
241
|
@classmethod
|
195
|
-
def identify_default_workspace_name(cls):
|
242
|
+
def identify_default_workspace_name(cls, workspaces_dir = None):
|
196
243
|
"""
|
197
244
|
Class method that reads default-workspace.toml to identify the default-workspace.
|
198
245
|
"""
|
199
|
-
|
200
|
-
|
246
|
+
if workspaces_dir is None:
|
247
|
+
workspaces_dir = cls.get_workspaces_dir()
|
201
248
|
logging.info(f"workspaces_dir = {workspaces_dir}\n")
|
202
249
|
default_toml_path = workspaces_dir / cls.DEFAULT_WORKSPACE_TOML_FILE_NAME
|
203
250
|
|
@@ -244,16 +291,15 @@ class WorkspaceManager:
|
|
244
291
|
return base / cls.APP_NAME
|
245
292
|
|
246
293
|
@classmethod
|
247
|
-
def
|
294
|
+
def ensure_appdata_workspaces_dir(cls) -> Path:
|
248
295
|
"""Create workspace folder and default toml if missing."""
|
249
|
-
workspaces_dir = cls.get_appdata_dir() /
|
296
|
+
workspaces_dir = cls.get_appdata_dir() / cls.WORKSPACES_DIR_NAME
|
250
297
|
workspaces_dir.mkdir(parents=True, exist_ok=True)
|
251
|
-
|
298
|
+
cls.workspaces_dir = workspaces_dir
|
252
299
|
default_file = workspaces_dir / cls.DEFAULT_WORKSPACE_TOML_FILE_NAME
|
253
300
|
if not default_file.exists():
|
254
301
|
default_file.write_text("# Default workspace config\n")
|
255
302
|
return workspaces_dir
|
256
|
-
|
257
303
|
|
258
304
|
def establish_default_workspace():
|
259
305
|
workspace_name = WorkspaceManager.identify_default_workspace_name()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: pipeline-eds
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.15
|
4
4
|
Summary: The official API pipeline library for mulch-based projects. Key target: Emerson Ovation EDS REST API.
|
5
5
|
License: BSD-3
|
6
6
|
Author: George Clayton Bennett
|
@@ -13,7 +13,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
14
14
|
Provides-Extra: windb
|
15
15
|
Requires-Dist: certifi (>=2025.1.31,<2026.0.0)
|
16
|
-
Requires-Dist: fastapi (
|
16
|
+
Requires-Dist: fastapi (>=0.116.1,<0.117.0)
|
17
|
+
Requires-Dist: matplotlib (>=3.10.6,<4.0.0)
|
17
18
|
Requires-Dist: mulch (>=0.2.8,<0.3.0)
|
18
19
|
Requires-Dist: mysql-connector-python (>=9.3.0,<10.0.0)
|
19
20
|
Requires-Dist: pendulum (>=3.1.0,<4.0.0)
|
@@ -1,17 +1,18 @@
|
|
1
1
|
pipeline/__init__.py,sha256=DAy4hBleDDk9Wen4qIOGlS03qWqdt7K7n4rUpL3cIL4,173
|
2
2
|
pipeline/__main__.py,sha256=TtbGYUm7Np5eLPCHZ3iX5QPL0rSgGJ5tXVuBEORvQIE,19
|
3
3
|
pipeline/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
pipeline/api/eds.py,sha256=
|
4
|
+
pipeline/api/eds.py,sha256=O6kHH9KsBKIXiLrT0Cj84g_UUIhjlK8vPPJbG2MP0DU,45087
|
5
5
|
pipeline/api/rjn.py,sha256=Fhbc3tE_WgUywVJimnLzhWnxyMzGiIUu54hRFR3sdUA,7102
|
6
6
|
pipeline/api/status_api.py,sha256=KJG7e4GXmTjqQxK3LFUcdAxj1jqzvNLJ0f9-tuisk00,202
|
7
7
|
pipeline/calls.py,sha256=FexXJK0_fwgQMPx9dy5eFai_6xqVOsOGoEUw8yP3eSU,4187
|
8
|
-
pipeline/cli.py,sha256=
|
8
|
+
pipeline/cli.py,sha256=0tfJE_ez-X4amyGHgsgwuiVQ2Ks8vH6nPEU-e9T-1XY,11708
|
9
9
|
pipeline/configrationmanager.py,sha256=UPqa91117jNm59vvTBE7sET1ThisqRf6B2Dhtk7x9tM,624
|
10
10
|
pipeline/decorators.py,sha256=5fIIVqxSvQFaSI4ZkqPd3yqajzDxaRhgYwlC1jD2k5A,411
|
11
11
|
pipeline/env.py,sha256=Ibs0K_wluUTJXCcBjUofZ1jexks-UmJ0dvbXhWPLdJ0,1922
|
12
|
-
pipeline/environment.py,sha256=
|
13
|
-
pipeline/gui_fastapi_plotly_live.py,sha256=
|
14
|
-
pipeline/gui_mpl_live.py,sha256=
|
12
|
+
pipeline/environment.py,sha256=ABXv5DAWW0EGrnE27dJNkj_aKmyD6e84RDSMyW1qKzE,1523
|
13
|
+
pipeline/gui_fastapi_plotly_live.py,sha256=bje-W93yWb7G7A5H7Rg7BgNpfV_fjxhbl7x7zwkUCXc,3246
|
14
|
+
pipeline/gui_mpl_live.py,sha256=JLh2YWK2SwGykik7mC_HfNMVzVVYymoEnwzSB7VHrIk,4286
|
15
|
+
pipeline/gui_plotly_static.py,sha256=wUNTjLqsuldWRYu65w5-HDco4201FKAO5esMe2NOras,1133
|
15
16
|
pipeline/helpers.py,sha256=-GGFz5t22wlFfHwtEDwWOBslzguLXXSYNzxxRBvclLI,4457
|
16
17
|
pipeline/install_appdata.py,sha256=DGemGRogMVAmFzqSKladfxgLQOslIHyul2Xxq34do8A,2814
|
17
18
|
pipeline/logging_setup.py,sha256=CAqWfchscCdzJ63Wf1dC3QGRnFnQqBELxyTmPZxsxgs,1674
|
@@ -21,7 +22,7 @@ pipeline/plotbuffer.py,sha256=jCsFbT47TdR8Sq5tjj2JdhVELjRiebLPN7O4r2LjPeY,625
|
|
21
22
|
pipeline/points_loader.py,sha256=4OCGLiatbP3D5hixVnYcFGThvBRYt_bf5DhNGdGw_9k,519
|
22
23
|
pipeline/queriesmanager.py,sha256=QwPhDV39Z8mdAVDRXTF4ZPgc6JliMgLzucGaGZSlDac,5001
|
23
24
|
pipeline/time_manager.py,sha256=gSK430SyHvhgUWLRg_z2nBiyad01v7ByyKafB138IkU,8351
|
24
|
-
pipeline/workspace_manager.py,sha256=
|
25
|
+
pipeline/workspace_manager.py,sha256=rK5VeEjnM9Xj32tFBQNYkvmVpMZMywA0LAWhRLc4Nu0,14213
|
25
26
|
workspaces/default-workspace.toml,sha256=dI8y2l2WlEbIck6IZpbuQUP8-Bf48bBE1CKKsnVMc8w,300
|
26
27
|
workspaces/eds_to_rjn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
28
|
workspaces/eds_to_rjn/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -56,8 +57,8 @@ workspaces/eds_to_rjn/scripts/daemon_runner.py,sha256=gBCYrJ-FYPCTt_l5O07_YNrrGj
|
|
56
57
|
workspaces/eds_to_rjn/secrets/README.md,sha256=tWf2bhopA0C08C8ImtHNZoPde9ub-sLMjX6EMe7lyJw,600
|
57
58
|
workspaces/eds_to_rjn/secrets/secrets-example.yaml,sha256=qKGrKsKBC0ulDQRVbr1zkfNlr8WPWK4lg5GAvTqZ-T4,365
|
58
59
|
workspaces/eds_to_termux/..txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
|
-
pipeline_eds-0.2.
|
60
|
-
pipeline_eds-0.2.
|
61
|
-
pipeline_eds-0.2.
|
62
|
-
pipeline_eds-0.2.
|
63
|
-
pipeline_eds-0.2.
|
60
|
+
pipeline_eds-0.2.15.dist-info/entry_points.txt,sha256=jmU0FQ7-2AHXhKcj4TXPn61xLbHlycHA2lkDlRZT-pg,124
|
61
|
+
pipeline_eds-0.2.15.dist-info/LICENSE,sha256=LKdx0wS1t9vFZpbRhDg_iLQ6ny-XsXRwhKAoCfrF6iA,1501
|
62
|
+
pipeline_eds-0.2.15.dist-info/METADATA,sha256=LLLqjRTN7PsOMP7NrsbZ2i2Dzb6uTJNYI5PsJnw75rI,10027
|
63
|
+
pipeline_eds-0.2.15.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
64
|
+
pipeline_eds-0.2.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|