Skip to content

Commit 7a5be9c

Browse files
authored
Merge pull request #1617 from AllenNeuralDynamics/develop
[production merge] 2025-09-26
2 parents 6555d67 + 8a5e73d commit 7a5be9c

3 files changed

Lines changed: 103 additions & 90 deletions

File tree

src/foraging_gui/Calibration.ui

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -451,38 +451,6 @@
451451
<string/>
452452
</property>
453453
</widget>
454-
<widget class="QPushButton" name="SaveLeft">
455-
<property name="geometry">
456-
<rect>
457-
<x>580</x>
458-
<y>20</y>
459-
<width>80</width>
460-
<height>25</height>
461-
</rect>
462-
</property>
463-
<property name="text">
464-
<string>Save</string>
465-
</property>
466-
<property name="checkable">
467-
<bool>true</bool>
468-
</property>
469-
</widget>
470-
<widget class="QPushButton" name="SaveRight">
471-
<property name="geometry">
472-
<rect>
473-
<x>580</x>
474-
<y>55</y>
475-
<width>80</width>
476-
<height>25</height>
477-
</rect>
478-
</property>
479-
<property name="text">
480-
<string>Save</string>
481-
</property>
482-
<property name="checkable">
483-
<bool>true</bool>
484-
</property>
485-
</widget>
486454
</widget>
487455
<widget class="QCheckBox" name="multi_value_enable">
488456
<property name="geometry">
@@ -866,8 +834,6 @@
866834
<zorder>WeightAfterRight</zorder>
867835
<zorder>TotalWaterSingleLeft</zorder>
868836
<zorder>TotalWaterSingleRight</zorder>
869-
<zorder>SaveLeft</zorder>
870-
<zorder>SaveRight</zorder>
871837
<zorder>label_21</zorder>
872838
<zorder>CalibrationType</zorder>
873839
<zorder>IntervalRight_2</zorder>

src/foraging_gui/Dialogs.py

Lines changed: 85 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,6 @@ def _connectSignalsSlots(self):
572572
lambda val: self.OpenRightForever.setDisabled(val)
573573
)
574574

575-
self.SaveLeft.clicked.connect(lambda: self._SaveValve("Left"))
576-
self.SaveRight.clicked.connect(lambda: self._SaveValve("Right"))
577575
self.StartCalibratingLeft.clicked.connect(self._StartCalibratingLeft)
578576
self.StartCalibratingRight.clicked.connect(self._StartCalibratingRight)
579577
self.Continue.clicked.connect(self._Continue)
@@ -896,34 +894,42 @@ def _CalibrateLeftOne(self, repeat=False):
896894
return
897895
self.WeightAfterLeft.setText(str(final_tube_weight))
898896

899-
# Mark measurement as complete, save data, and update figure
900-
self.left_measurements[next_index] = True
901-
self._Save(
902-
valve="Left",
903-
valve_open_time=str(current_valve_opentime),
904-
valve_open_interval=str(self.params["Interval"]),
905-
cycle=str(self.params["Cycle"]),
906-
total_water=float(final_tube_weight),
907-
tube_weight=float(before_weight),
908-
)
909-
self._UpdateFigure()
910-
911-
# Direct user for next steps
912-
if np.all(self.left_measurements):
913-
self.Warning.setText(
914-
"Measurements recorded for all values. Please press Repeat, or Finished"
915-
)
916-
self.Repeat.setStyleSheet("color: black;background-color : none;")
917-
self.Finished.setStyleSheet(
918-
"color: white;background-color : mediumorchid;"
897+
if self.check_calibration_curve(float(final_tube_weight), float(before_weight)):
898+
# Mark measurement as complete, save data, and update figure
899+
self.left_measurements[next_index] = True
900+
self._Save(
901+
valve="Left",
902+
valve_open_time=str(current_valve_opentime),
903+
valve_open_interval=str(self.params["Interval"]),
904+
cycle=str(self.params["Cycle"]),
905+
total_water=float(final_tube_weight),
906+
tube_weight=float(before_weight),
919907
)
908+
self._UpdateFigure()
909+
910+
# Direct user for next steps
911+
if np.all(self.left_measurements):
912+
self.Warning.setText(
913+
"Measurements recorded for all values. Please press Repeat, or Finished"
914+
)
915+
self.Repeat.setStyleSheet("color: black;background-color : none;")
916+
self.Finished.setStyleSheet(
917+
"color: white;background-color : mediumorchid;"
918+
)
919+
else:
920+
self.Warning.setText("Please press Continue, Repeat, or Finished")
921+
self.Continue.setStyleSheet(
922+
"color: white;background-color : mediumorchid;"
923+
)
924+
self.Repeat.setStyleSheet("color: black;background-color : none;")
920925
else:
921-
self.Warning.setText("Please press Continue, Repeat, or Finished")
922-
self.Continue.setStyleSheet(
923-
"color: white;background-color : mediumorchid;"
926+
self.Warning.setText(
927+
"Recorded before and after tube weights do not pass checks. Please repeat check."
928+
"Value has not been saved."
924929
)
925930
self.Repeat.setStyleSheet("color: black;background-color : none;")
926931

932+
927933
def _StartCalibratingRight(self):
928934
"""start the calibration loop of right valve"""
929935

@@ -1083,31 +1089,59 @@ def _CalibrateRightOne(self, repeat=False):
10831089
return
10841090
self.WeightAfterRight.setText(str(final_tube_weight))
10851091

1086-
# Mark measurement as complete, save data, and update figure
1087-
self.right_measurements[next_index] = True
1088-
self._Save(
1089-
valve="Right",
1090-
valve_open_time=str(current_valve_opentime),
1091-
valve_open_interval=str(self.params["Interval"]),
1092-
cycle=str(self.params["Cycle"]),
1093-
total_water=float(final_tube_weight),
1094-
tube_weight=float(before_weight),
1095-
)
1096-
self._UpdateFigure()
1097-
1098-
# Direct user for next steps
1099-
if np.all(self.right_measurements):
1100-
self.Warning.setText(
1101-
"Measurements recorded for all values. Please press Repeat, or Finished"
1092+
if self.check_calibration_curve(float(final_tube_weight), float(before_weight)):
1093+
# Mark measurement as complete, save data, and update figure
1094+
self.right_measurements[next_index] = True
1095+
self._Save(
1096+
valve="Right",
1097+
valve_open_time=str(current_valve_opentime),
1098+
valve_open_interval=str(self.params["Interval"]),
1099+
cycle=str(self.params["Cycle"]),
1100+
total_water=float(final_tube_weight),
1101+
tube_weight=float(before_weight),
11021102
)
1103-
self.Repeat.setStyleSheet("color: black;background-color : none;")
1103+
self._UpdateFigure()
1104+
1105+
# Direct user for next steps
1106+
if np.all(self.right_measurements):
1107+
self.Warning.setText(
1108+
"Measurements recorded for all values. Please press Repeat, or Finished"
1109+
)
1110+
self.Repeat.setStyleSheet("color: black;background-color : none;")
1111+
else:
1112+
self.Warning.setText("Please press Continue, Repeat, or Finished")
1113+
self.Continue.setStyleSheet(
1114+
"color: white;background-color : mediumorchid;"
1115+
)
1116+
self.Repeat.setStyleSheet("color: black;background-color : none;")
11041117
else:
1105-
self.Warning.setText("Please press Continue, Repeat, or Finished")
1106-
self.Continue.setStyleSheet(
1107-
"color: white;background-color : mediumorchid;"
1118+
self.Warning.setText(
1119+
"Recorded before and after tube weights do not pass checks. Please repeat check."
1120+
"Value has not been saved."
11081121
)
11091122
self.Repeat.setStyleSheet("color: black;background-color : none;")
11101123

1124+
def check_calibration_curve(self,
1125+
after_tube_weight: float,
1126+
init_tube_weight: float) -> bool:
1127+
1128+
"""
1129+
Check that values are positive and will not result in negative curve
1130+
1131+
:param after_tube_weight: tube weight after calibration
1132+
:param init_tube_weight: tube weight before calibration
1133+
1134+
:returns boolean indicating whether values passed check
1135+
"""
1136+
1137+
if not after_tube_weight >= 0 or not init_tube_weight >= 0:
1138+
return False
1139+
1140+
if after_tube_weight <= init_tube_weight:
1141+
return False
1142+
1143+
return True
1144+
11111145
def _CalibrationStatus(self, opentime, weight_before, i, cycle, interval):
11121146
self.Warning.setText(
11131147
"Measuring left valve: {}s".format(opentime)
@@ -1174,6 +1208,7 @@ def _Save(
11741208
WaterCalibrationResults[date_str][valve][valve_open_time][
11751209
valve_open_interval
11761210
][cycle] = [np.round(total_water, 1)]
1211+
11771212
self.WaterCalibrationResults = WaterCalibrationResults.copy()
11781213

11791214
# save to the json file
@@ -1187,6 +1222,11 @@ def _Save(
11871222
# update the figure
11881223
self._UpdateFigure()
11891224

1225+
# update calibration parameters ui uses
1226+
self.MainWindow._GetWaterCalibration()
1227+
FittingResults = self.PlotM.FittingResults
1228+
self.MainWindow._GetLatestFitting(FittingResults)
1229+
11901230
def _UpdateFigure(self):
11911231
"""plot the calibration result"""
11921232
if self.ToInitializeVisual == 1: # only run once
@@ -1322,7 +1362,6 @@ def _SpotCheck(self, valve: Literal["Left", "Right"]):
13221362
"""
13231363

13241364
spot_check = getattr(self, f"SpotCheck{valve}")
1325-
save = getattr(self, f"Save{valve}")
13261365
total_water = getattr(self, f"TotalWaterSingle{valve}")
13271366
pre_weight = getattr(self, f"SpotCheckPreWeight{valve}")
13281367
volume = getattr(self, f"Spot{valve}Volume").text()
@@ -1331,7 +1370,6 @@ def _SpotCheck(self, valve: Literal["Left", "Right"]):
13311370
if self.MainWindow.InitializeBonsaiSuccessfully == 0:
13321371
spot_check.setChecked(False)
13331372
spot_check.setStyleSheet("background-color : none;")
1334-
save.setStyleSheet("color: black;background-color : none;")
13351373
total_water.setText("")
13361374
pre_weight.setText("")
13371375
return
@@ -1352,7 +1390,6 @@ def _SpotCheck(self, valve: Literal["Left", "Right"]):
13521390
self.Warning.setText("")
13531391
pre_weight.setText("")
13541392
total_water.setText("")
1355-
save.setStyleSheet("color: black;background-color : none;")
13561393
return
13571394

13581395
logging.info(f"starting spot check {valve.lower()}")
@@ -1380,7 +1417,6 @@ def _SpotCheck(self, valve: Literal["Left", "Right"]):
13801417
self.Warning.setText(f"Spot check {valve.lower()} cancelled")
13811418
pre_weight.setText("")
13821419
total_water.setText("")
1383-
save.setStyleSheet("color: black;background-color : none;")
13841420
return
13851421
pre_weight.setText(str(empty_tube_weight))
13861422

@@ -1490,9 +1526,6 @@ def _SpotCheck(self, valve: Literal["Left", "Right"]):
14901526
valve, error
14911527
)
14921528
)
1493-
save.setStyleSheet(
1494-
"color: white;background-color : mediumorchid;"
1495-
)
14961529
self.Warning.setText(
14971530
f"Measuring {valve.lower()} valve: {volume}uL"
14981531
+ "\nEmpty tube weight: {}g".format(empty_tube_weight)

src/foraging_gui/Foraging.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,12 +1569,17 @@ def _LoadSchedule(self):
15691569

15701570
# Find the correct week on the schedule
15711571
dividers = schedule[[isinstance(x,str)and('/' in x) for x in schedule['Mouse ID'].values]]
1572-
today = datetime.now().strftime('%m/%d/%Y')
1572+
today = datetime.now()
15731573

15741574
# Multiple weeks on the schedule
15751575
if len(dividers) > 1:
1576-
first = dividers.iloc[0]['Mouse ID']
1577-
if datetime.strptime(today, "%m/%d/%Y") < datetime.strptime(first,"%m/%d/%Y"):
1576+
first = datetime.strptime(dividers.iloc[0]['Mouse ID'], "%m/%d/%Y")
1577+
1578+
# switch schedule at Friday 5pm
1579+
cutoff = first - timedelta(days=3) # Go back to Friday
1580+
cutoff = cutoff.replace(hour=17, minute=0, second=0, microsecond=0) # 5 PM
1581+
1582+
if today < cutoff:
15781583
# Use last weeks schedule
15791584
schedule = schedule.loc[dividers.index.values[1]:]
15801585
else:
@@ -4111,8 +4116,17 @@ def _Save(self, ForceSave=0, SaveAs=0, SaveContinue=0, BackupSave=0):
41114116
with open(self.SaveFile, "w") as outfile:
41124117
json.dump(Obj2, outfile, indent=4, cls=NumpyEncoder)
41134118
elif self.SaveFile.endswith(".json"):
4114-
with open(self.SaveFile, "w") as outfile:
4119+
# Crashses during save can corupt a json file.
4120+
# Make tmp file to save to
4121+
tmp_file_name = self.SaveFile.split('.json')[0]
4122+
tmp_file_name = tmp_file_name + '_tmp.json'
4123+
with open(tmp_file_name, "w") as outfile:
41154124
json.dump(Obj, outfile, indent=4, cls=NumpyEncoder)
4125+
# After file is safely saved, remove the old save file
4126+
# and rewrite the new one.
4127+
if os.path.isfile(self.SaveFile):
4128+
os.remove(self.SaveFile)
4129+
os.rename(tmp_file_name,self.SaveFile)
41164130

41174131
# Toggle unsaved data to False
41184132
if BackupSave == 0:

0 commit comments

Comments
 (0)