Skip to content

Commit 593394c

Browse files
authored
Merge pull request #654 from czbiohub-sf/update-study-form-csv
Update study form csv
2 parents a43d617 + b88bed3 commit 593394c

File tree

3 files changed

+82
-27
lines changed

3 files changed

+82
-27
lines changed

ulc_mm_package/QtGUI/oracle.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ def _maybe_start_study_form(self):
646646

647647
def get_study_metadata(self):
648648
self.study_metadata = self.study_form_dialog.get_form_input()
649+
self.study_form_dialog.close()
649650
self.save_form()
650651

651652
def save_form(self):

ulc_mm_package/QtGUI/study_metadata_form.py

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from pathlib import Path
2-
from typing import Dict, List, Optional
2+
from typing import Dict, List, Optional, Tuple
33

44
from PyQt5.QtCore import QDate
55
from PyQt5.QtGui import QIntValidator
@@ -84,7 +84,6 @@ def create_widget_for_field(field_def):
8484

8585
default = field_def.get("default")
8686
if default is not None:
87-
print("default is")
8887
idx = w.findText(default)
8988
if idx >= 0:
9089
w.setCurrentIndex(idx)
@@ -139,7 +138,7 @@ def __init__(self, cfg: dict, parent=None):
139138
self.btn_start.setEnabled(False)
140139
layout.addRow(label, widget)
141140

142-
self.btn_start.clicked.connect(self.accept)
141+
self.btn_start.clicked.connect(self.validate)
143142
btn_cancel.clicked.connect(self.close)
144143

145144
layout.addRow(btn_cancel, self.btn_start)
@@ -162,34 +161,76 @@ def __init__(self, cfg: dict, parent=None):
162161
# run an initial check to set OK button state
163162
self.check_required()
164163

164+
def _get_widget_text(self, w: QWidget) -> Optional[str]:
165+
if isinstance(w, QTextEdit):
166+
return w.toPlainText()
167+
elif isinstance(w, QLineEdit):
168+
return w.text()
169+
elif isinstance(w, QComboBox):
170+
return "" if w.currentIndex() < 0 else w.currentText()
171+
elif isinstance(w, QListWidget):
172+
return "\n".join(i.text() for i in w.selectedItems())
173+
174+
if hasattr(w, "text"):
175+
return w.text()
176+
return None
177+
178+
def _is_empty(self, w) -> bool:
179+
text = self._get_widget_text(w)
180+
return (text is not None) and (not text.strip())
181+
182+
def _has_value_and_valid(self, w: QWidget) -> bool:
183+
if self._is_empty(w):
184+
return True
185+
186+
if isinstance(w, QTextEdit):
187+
return bool(w.toPlainText().strip())
188+
elif isinstance(w, QLineEdit):
189+
text = w.text().strip()
190+
return bool(text) and w.hasAcceptableInput()
191+
elif isinstance(w, QComboBox):
192+
return w.currentIndex() >= 0
193+
elif isinstance(w, QListWidget):
194+
return len(w.selectedItems()) >= 0
195+
196+
if hasattr(w, "text"):
197+
return bool(w.text().strip())
198+
199+
return False
200+
201+
def _check_widget(self, w: QWidget, field: dict) -> Tuple[bool, str]:
202+
required = field.get("required")
203+
if self._is_empty(w) and required:
204+
return False, "Required"
205+
206+
if not self._has_value_and_valid(w):
207+
return False, "Invalid"
208+
209+
return True, ""
210+
165211
def check_required(self):
166212
"""Ensure that all required widgets have a value."""
167-
all_filled = True
168-
for w in self._required_widgets:
169-
if hasattr(w, "toPlainText"):
170-
if not w.toPlainText().strip():
171-
all_filled = False
172-
break
173-
elif hasattr(w, "text"):
174-
if not w.text().strip():
175-
all_filled = False
176-
break
177-
elif hasattr(w, "currentIndex"):
178-
# -1 index means no selection made
179-
if w.currentIndex() < 0:
180-
all_filled = False
181-
break
182-
if all_filled:
183-
self.btn_start.setEnabled(True)
184-
else:
185-
self.btn_start.setEnabled(False)
213+
self.btn_start.setEnabled(
214+
all([self._has_value_and_valid(x) for x in self._required_widgets])
215+
)
216+
217+
def validate(self):
218+
for lbl, (w, field) in self._widgets.items():
219+
valid, error_msg = self._check_widget(w, field)
220+
if not valid:
221+
print(lbl, field, error_msg)
222+
w.setFocus()
223+
return
224+
self.accept()
186225

187226
def get_form_input(self):
188227
result = {"study_id": self.config_data["study_description"]["key"]}
189228
errors = []
190-
for name, (widget, field) in self._widgets.items():
229+
possible_choices = []
230+
subkeys = []
231+
for _, (widget, field) in self._widgets.items():
191232
t = field["datatype"]
192-
233+
key = field.get("key")
193234
if t == "string":
194235
value = (
195236
widget.toPlainText() if field.get("multiline") else widget.text()
@@ -209,10 +250,20 @@ def get_form_input(self):
209250
value = widget.currentData()
210251
elif t == "multiselect":
211252
value = [x.text() for x in widget.selectedItems()]
253+
possible_choices = [
254+
widget.item(i).text() for i in range(widget.count())
255+
]
256+
subkeys = field["subkeys"]
212257
else:
213258
value = None
214259

215-
result[name] = value
260+
# Accommodate the multiselect case separately (i.e. save each choice as its own col)
261+
if isinstance(value, list):
262+
selected_set = set(value)
263+
for choice, choice_key in zip(possible_choices, subkeys):
264+
result[choice_key] = choice in selected_set
265+
else:
266+
result[key] = value
216267

217268
if errors:
218269
raise ValueError("\n".join(errors))

ulc_mm_package/study_configurations/_example-config.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ name = "Example Study"
44

55
# Metadata fields
66
[[metadata]]
7-
key = "key1_blah"
7+
key = "key1"
88
label = "numbers galore"
99
datatype = "int"
1010
required = true
1111

1212
[[metadata]]
13-
key = "key2_blah"
13+
key = "key2"
1414
label = "string stuff"
1515
datatype = "string"
1616
required = true
@@ -21,12 +21,15 @@ label = "choices"
2121
datatype = "enum"
2222
choices = ["control", "low_dose", "high_dose"]
2323
default = "control"
24+
required = true
2425

2526
[[metadata]]
2627
key = "key4"
2728
label = "Multiselect"
2829
datatype = "multiselect"
2930
choices = ["pickme", "orme", "orwho", "or", "allofus"]
31+
subkeys = ["choice1", "choice2", "choice3", "choice4", "choice5"]
32+
required = false
3033

3134

3235
[[metadata]]

0 commit comments

Comments
 (0)