Rename Sud's Download/Upload to Save/Load throughout

Renames everywhere the concept appears: Sud.download()/upload() ->
save()/load(), the 'Download'/'Upload' websocket messages ->
'Save'/'Load', the brewpi_gui menu actions, handlers and
sud_download_path -> sud_save_path, and the demo script. Behavior is
unchanged - this is naming only, from the client's perspective (save
to / load from a local file) rather than the server's.
This commit is contained in:
2026-06-20 23:58:21 +02:00
parent 5d884ca3da
commit 13e507e2b5
6 changed files with 51 additions and 51 deletions
+6 -6
View File
@@ -3712,8 +3712,8 @@
<property name="title"> <property name="title">
<string>&amp;File</string> <string>&amp;File</string>
</property> </property>
<addaction name="actionSudDownload"/> <addaction name="actionSudSave"/>
<addaction name="actionSudUpload"/> <addaction name="actionSudLoad"/>
</widget> </widget>
<widget class="QMenu" name="menu_Help"> <widget class="QMenu" name="menu_Help">
<property name="title"> <property name="title">
@@ -3765,17 +3765,17 @@
<string>Stop</string> <string>Stop</string>
</property> </property>
</action> </action>
<action name="actionSudDownload"> <action name="actionSudSave">
<property name="text"> <property name="text">
<string>&amp;Download Sud...</string> <string>&amp;Save Sud...</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Save the running Sud's schedule to a local sud.json</string> <string>Save the running Sud's schedule to a local sud.json</string>
</property> </property>
</action> </action>
<action name="actionSudUpload"> <action name="actionSudLoad">
<property name="text"> <property name="text">
<string>&amp;Upload Sud...</string> <string>&amp;Load Sud...</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Replace the running Sud's schedule with a local sud.json</string> <string>Replace the running Sud's schedule with a local sud.json</string>
+15 -15
View File
@@ -209,8 +209,8 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
self.actionStart.triggered.connect(self.on_action_sud_start) self.actionStart.triggered.connect(self.on_action_sud_start)
self.actionPause.triggered.connect(self.on_action_sud_pause) self.actionPause.triggered.connect(self.on_action_sud_pause)
self.actionStop.triggered.connect(self.on_action_sud_stop) self.actionStop.triggered.connect(self.on_action_sud_stop)
self.actionSudDownload.triggered.connect(self.on_action_sud_download) self.actionSudSave.triggered.connect(self.on_action_sud_save)
self.actionSudUpload.triggered.connect(self.on_action_sud_upload) self.actionSudLoad.triggered.connect(self.on_action_sud_load)
self.Slider_pwr_soll.valueChanged.connect(self.on_slider_pwr_soll_changed) self.Slider_pwr_soll.valueChanged.connect(self.on_slider_pwr_soll_changed)
self.Slider_temp_soll.valueChanged.connect(self.on_slider_temp_soll_changed) self.Slider_temp_soll.valueChanged.connect(self.on_slider_temp_soll_changed)
self.Slider_speed_soll.valueChanged.connect(self.on_slider_stirrer_speed_soll_changed) self.Slider_speed_soll.valueChanged.connect(self.on_slider_stirrer_speed_soll_changed)
@@ -227,7 +227,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
self.checkbox_heater_activate_initial_update = True self.checkbox_heater_activate_initial_update = True
self.checkbox_stirrer_activate_initial_update = True self.checkbox_stirrer_activate_initial_update = True
self.heatrate_soll_initial_update = True self.heatrate_soll_initial_update = True
self.sud_download_path = None self.sud_save_path = None
# Latest values sampled by the plot timer - written from the # Latest values sampled by the plot timer - written from the
# websocket recv thread, read from the GUI thread; plain floats so # websocket recv thread, read from the GUI thread; plain floats so
@@ -263,10 +263,10 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
self.plot.reset() self.plot.reset()
self.ws_client.connect(uri=self.plainTextUri.toPlainText()) self.ws_client.connect(uri=self.plainTextUri.toPlainText())
# Fetch the schedule for the Automatic tab's forecast preview - not # Fetch the schedule for the Automatic tab's forecast preview - not
# a user-initiated download, so sud_download_path stays None and # a user-initiated save, so sud_save_path stays None and
# on_sud_changed() routes the reply to update_sud_forecast() instead # on_sud_changed() routes the reply to update_sud_forecast() instead
# of a save dialog. # of a save dialog.
self.msg_sud.send({'Download': True}) self.msg_sud.send({'Save': True})
def on_plot_timer(self): def on_plot_timer(self):
self.plot.sample( self.plot.sample(
@@ -352,23 +352,23 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
def on_action_sud_stop(self): def on_action_sud_stop(self):
self.msg_sud.send({'Stop': True}) self.msg_sud.send({'Stop': True})
def on_action_sud_download(self): def on_action_sud_save(self):
# Pick the destination up front, on the GUI thread - the actual # Pick the destination up front, on the GUI thread - the actual
# write happens in on_sud_changed(), which runs off a worker thread # write happens in on_sud_changed(), which runs off a worker thread
# and must not touch Qt (e.g. open a file dialog) itself. # and must not touch Qt (e.g. open a file dialog) itself.
path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Download Sud", filter="JSON files (*.json)") path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save Sud", filter="JSON files (*.json)")
if not path: if not path:
return return
self.sud_download_path = path self.sud_save_path = path
self.msg_sud.send({'Download': True}) self.msg_sud.send({'Save': True})
def on_action_sud_upload(self): def on_action_sud_load(self):
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Upload Sud", filter="JSON files (*.json)") path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Load Sud", filter="JSON files (*.json)")
if not path: if not path:
return return
with open(path) as f: with open(path) as f:
data = json.load(f) data = json.load(f)
self.msg_sud.send({'Upload': data}) self.msg_sud.send({'Load': data})
def on_pot_changed(self, msg): def on_pot_changed(self, msg):
print("on_pot_changed {}".format(msg)) print("on_pot_changed {}".format(msg))
@@ -458,10 +458,10 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
self.sud_loaded = True self.sud_loaded = True
for key in msg: for key in msg:
if "Json" in key: if "Json" in key:
if self.sud_download_path: if self.sud_save_path:
with open(self.sud_download_path, 'w') as f: with open(self.sud_save_path, 'w') as f:
json.dump(msg['Json'], f, indent='\t') json.dump(msg['Json'], f, indent='\t')
self.sud_download_path = None self.sud_save_path = None
else: else:
self.update_sud_forecast(msg['Json']) self.update_sud_forecast(msg['Json'])
elif "Name" in key: elif "Name" in key:
+10 -10
View File
@@ -1255,12 +1255,12 @@ class Ui_MainWindow(object):
self.actionPause.setObjectName("actionPause") self.actionPause.setObjectName("actionPause")
self.actionStop = QtWidgets.QAction(MainWindow) self.actionStop = QtWidgets.QAction(MainWindow)
self.actionStop.setObjectName("actionStop") self.actionStop.setObjectName("actionStop")
self.actionSudDownload = QtWidgets.QAction(MainWindow) self.actionSudSave = QtWidgets.QAction(MainWindow)
self.actionSudDownload.setObjectName("actionSudDownload") self.actionSudSave.setObjectName("actionSudSave")
self.actionSudUpload = QtWidgets.QAction(MainWindow) self.actionSudLoad = QtWidgets.QAction(MainWindow)
self.actionSudUpload.setObjectName("actionSudUpload") self.actionSudLoad.setObjectName("actionSudLoad")
self.menu_File.addAction(self.actionSudDownload) self.menu_File.addAction(self.actionSudSave)
self.menu_File.addAction(self.actionSudUpload) self.menu_File.addAction(self.actionSudLoad)
self.menubar.addAction(self.menu_File.menuAction()) self.menubar.addAction(self.menu_File.menuAction())
self.menubar.addAction(self.menu_Help.menuAction()) self.menubar.addAction(self.menu_Help.menuAction())
self.toolBar.addAction(self.actionStart) self.toolBar.addAction(self.actionStart)
@@ -1306,7 +1306,7 @@ class Ui_MainWindow(object):
self.actionPause.setToolTip(_translate("MainWindow", "Pause")) self.actionPause.setToolTip(_translate("MainWindow", "Pause"))
self.actionStop.setText(_translate("MainWindow", "Stop")) self.actionStop.setText(_translate("MainWindow", "Stop"))
self.actionStop.setToolTip(_translate("MainWindow", "Stop")) self.actionStop.setToolTip(_translate("MainWindow", "Stop"))
self.actionSudDownload.setText(_translate("MainWindow", "&Download Sud...")) self.actionSudSave.setText(_translate("MainWindow", "&Save Sud..."))
self.actionSudDownload.setToolTip(_translate("MainWindow", "Save the running Sud\'s schedule to a local sud.json")) self.actionSudSave.setToolTip(_translate("MainWindow", "Save the running Sud\'s schedule to a local sud.json"))
self.actionSudUpload.setText(_translate("MainWindow", "&Upload Sud...")) self.actionSudLoad.setText(_translate("MainWindow", "&Load Sud..."))
self.actionSudUpload.setToolTip(_translate("MainWindow", "Replace the running Sud\'s schedule with a local sud.json")) self.actionSudLoad.setToolTip(_translate("MainWindow", "Replace the running Sud\'s schedule with a local sud.json"))
+5 -5
View File
@@ -63,7 +63,7 @@ class Sud(AttributeChange):
def _parse_data(data): def _parse_data(data):
"""Parses a sud.json document into the (name, description, schedule, """Parses a sud.json document into the (name, description, schedule,
pot_mass, pot_material) tuple Sud needs. Computed up front rather pot_mass, pot_material) tuple Sud needs. Computed up front rather
than assigned straight onto self, so a malformed upload() can't than assigned straight onto self, so a malformed load() can't
leave a half-applied schedule in place.""" leave a half-applied schedule in place."""
name = data.get('Name', '') name = data.get('Name', '')
description = data.get('Description', '') description = data.get('Description', '')
@@ -78,7 +78,7 @@ class Sud(AttributeChange):
def _reset_run_state(self): def _reset_run_state(self):
"""Resets run-time progress back to a freshly-loaded, not-yet-started """Resets run-time progress back to a freshly-loaded, not-yet-started
IDLE state. Shared by __init__, upload(), and stop().""" IDLE state. Shared by __init__, load(), and stop()."""
self.index = -1 self.index = -1
self.hold_remaining = 0.0 self.hold_remaining = 0.0
self.state = SudState.IDLE self.state = SudState.IDLE
@@ -86,13 +86,13 @@ class Sud(AttributeChange):
self.user_message = None self.user_message = None
self._paused_from = None self._paused_from = None
def download(self): def save(self):
"""Returns the sud.json document currently on disk, for a client to """Returns the sud.json document currently on disk, for a client to
edit and hand back to upload().""" edit and hand back to load()."""
with open(self.path) as f: with open(self.path) as f:
return json.load(f) return json.load(f)
def upload(self, data): def load(self, data):
"""Replaces the schedule with a new sud.json document and persists """Replaces the schedule with a new sud.json document and persists
it to disk, resetting to IDLE as if freshly loaded from that file. it to disk, resetting to IDLE as if freshly loaded from that file.
Refused while a brew is in progress, or if data is malformed - in Refused while a brew is in progress, or if data is malformed - in
@@ -5,7 +5,7 @@ from pathlib import Path
from components.sud import Sud, SudState from components.sud import Sud, SudState
# Console-only walkthrough of Sud.download()/upload() (no plot - there's no # Console-only walkthrough of Sud.save()/load() (no plot - there's no
# numeric data here, just JSON plumbing). Runs against a scratch copy of a # numeric data here, just JSON plumbing). Runs against a scratch copy of a
# real sud.json so sude/sud_0010.json itself is never touched. # real sud.json so sude/sud_0010.json itself is never touched.
@@ -17,29 +17,29 @@ if __name__ == '__main__':
sud = Sud(str(path)) sud = Sud(str(path))
print(f"Loaded '{sud.name}', {len(sud.schedule)} steps") print(f"Loaded '{sud.name}', {len(sud.schedule)} steps")
doc = sud.download() doc = sud.save()
assert doc['Name'] == sud.name assert doc['Name'] == sud.name
print("download() returned the on-disk document") print("save() returned the on-disk document")
sud.state = SudState.RAMPING sud.state = SudState.RAMPING
assert sud.upload(doc) is False assert sud.load(doc) is False
print("upload() refused while RAMPING") print("load() refused while RAMPING")
sud.state = SudState.IDLE sud.state = SudState.IDLE
doc['Name'] = 'Sud-Renamed' doc['Name'] = 'Sud-Renamed'
doc['steps'][0]['descr'] = 'Edited step' doc['steps'][0]['descr'] = 'Edited step'
assert sud.upload(doc) is True assert sud.load(doc) is True
assert sud.name == 'Sud-Renamed' assert sud.name == 'Sud-Renamed'
assert sud.schedule[0]['descr'] == 'Edited step' assert sud.schedule[0]['descr'] == 'Edited step'
assert sud.state == SudState.IDLE assert sud.state == SudState.IDLE
print("upload() applied the edit and reset to IDLE") print("load() applied the edit and reset to IDLE")
on_disk = json.loads(path.read_text()) on_disk = json.loads(path.read_text())
assert on_disk['Name'] == 'Sud-Renamed' assert on_disk['Name'] == 'Sud-Renamed'
print("upload() persisted the edit to disk") print("load() persisted the edit to disk")
assert sud.upload({'Name': 'broken'}) is False assert sud.load({'Name': 'broken'}) is False
assert sud.name == 'Sud-Renamed' assert sud.name == 'Sud-Renamed'
print("upload() rejected malformed data, left schedule untouched") print("load() rejected malformed data, left schedule untouched")
print("All sud upload/download checks passed") print("All sud save/load checks passed")
+4 -4
View File
@@ -73,10 +73,10 @@ class SudTask(ATask):
self.sud.pause() self.sud.pause()
elif 'Stop' in pair[0]: elif 'Stop' in pair[0]:
self.sud.stop() self.sud.stop()
elif 'Download' in pair[0]: elif 'Save' in pair[0]:
await self.send({'Json': self.sud.download()}) await self.send({'Json': self.sud.save()})
elif 'Upload' in pair[0]: elif 'Load' in pair[0]:
if self.sud.upload(pair[1]): if self.sud.load(pair[1]):
await self.send({'Name': self.sud.name, 'Description': self.sud.description}) await self.send({'Name': self.sud.name, 'Description': self.sud.description})
await self.send({'Json': pair[1]}) await self.send({'Json': pair[1]})