Skip to content

Commit ff8a116

Browse files
committed
Docstrings and formatting
1 parent 4b59726 commit ff8a116

File tree

2 files changed

+94
-40
lines changed

2 files changed

+94
-40
lines changed

pyhdx/panel/controller.py

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -98,29 +98,35 @@ def publish_data(self, name, data_source_obj):
9898

9999
class PyHDXController(MainController):
100100
"""
101-
Main controller for PyHDX web application
101+
Main controller for PyHDX web application.
102102
103103
"""
104-
fit_results = param.Dict({}, doc='Dictionary of fit results')
105-
peptides = param.ClassSelector(PeptideMasterTable, doc='Master list of all peptides')
106-
series = param.ClassSelector(KineticsSeries, doc='KineticsSeries object with current selected and corrected peptides')
104+
fit_results = param.Dict({}, doc='Dictionary of fit results', precedence=-1)
105+
peptides = param.ClassSelector(PeptideMasterTable, doc='Master list of all peptides', precedence=-1)
106+
series = param.ClassSelector(KineticsSeries,
107+
doc='KineticsSeries object with current selected and corrected peptides', precedence=-1)
107108

108109
def __init__(self, *args, **kwargs):
109110
super(PyHDXController, self).__init__(*args, **kwargs)
110-
#setup options #todo automate figure out cross dependencies (via parent?)
111-
self.control_panels['OptionsControl'].link_xrange = True
112111

113112

114113
class ComparisonController(MainController):
115114
"""
116-
Main controller for binary comparison web application
115+
Main controller for binary comparison web application.
117116
"""
118117

119-
datasets = param.Dict(default={}, doc='Dictionary for all datasets') #todo refactor
118+
datasets = param.Dict(default={}, doc='Dictionary for all datasets')
120119
comparisons = param.Dict(default={}, doc='Dictionary for all comparisons (should be in sources)')
121120

122121

123122
class MappingFileInputControl(ControlPanel):
123+
"""
124+
This controller allows users to upload *.txt files where quantities (protection factors, Gibbs free energy, etc) are
125+
mapped to a linear sequence.
126+
127+
The column should be tab separated with on the last header line (starts with '#') the names of the columns. Columns
128+
should be tab-delimited.
129+
"""
124130
header = 'File Input'
125131

126132
input_file = param.Parameter(default=None, doc='Input file to add to available datasets')
@@ -143,7 +149,6 @@ def _input_file_updated(self):
143149
self.dataset_name = self.dataset_name or Path(self._widget_dict['input_file'].filename).stem
144150

145151
def _action_add_dataset(self):
146-
print('action add')
147152
if self.dataset_name in self.parent.datasets.keys():
148153
self.parent.logger.info(f'Dataset {self.dataset_name} already added')
149154
elif not self.input_file:
@@ -173,19 +178,26 @@ def _datasets_updated(self, events):
173178

174179

175180
class DifferenceControl(ControlPanel):
181+
"""
182+
This controller allows users to select two datasets from available datasets, choose a quantity to compare between,
183+
and choose the type of operation between quantities (Subtract/Divide).
184+
185+
"""
176186
header = 'Differences'
177187

178-
dataset_1 = param.Selector(doc='ds1')
179-
dataset_2 = param.Selector(doc='ds2')
188+
dataset_1 = param.Selector(doc='First dataset to compare')
189+
dataset_2 = param.Selector(doc='Second dataset to compare')
180190

181191
comparison_name = param.String()
182-
operation = param.Selector(default='Subtract', objects=['Subtract', 'Divide'])
192+
operation = param.Selector(default='Subtract', objects=['Subtract', 'Divide'],
193+
doc='Select the operation to perform between the two datasets')
183194

184195
comparison_quantity = param.Selector(doc="Select a quantity to compare (column from input txt file)")
185196
add_comparison = param.Action(lambda self: self._action_add_comparison(),
186197
doc='Click to add this comparison to available comparisons')
187198
comparison_list = param.ListSelector(doc='Lists available comparisons')
188-
remove_comparison = param.Action(lambda self: self._action_remove_comparison())
199+
remove_comparison = param.Action(lambda self: self._action_remove_comparison(),
200+
doc='Remove selected comparisons from the list')
189201

190202
def __init__(self, parent, **params):
191203
super(DifferenceControl, self).__init__(parent, **params)
@@ -207,6 +219,7 @@ def _selection_updated(self):
207219
datasets = (self.parent.datasets[self.dataset_1], self.parent.datasets[self.dataset_2]) # property?
208220
unique_names = set.intersection(*[{name for name in array.dtype.names} for array in datasets])
209221

222+
#todo check for scalar-type dtype
210223
objects = [name for name in unique_names if name != 'r_number']
211224
self.param['comparison_quantity'].objects = objects
212225
if self.comparison_quantity is None:
@@ -255,21 +268,20 @@ def _update_comparison_list(self):
255268
self.param['comparison_list'].objects = objects
256269

257270

258-
# def _action_remove_comparison(self):
259-
# for comparison_name in self.comparison_list:
260-
# self.parent.comparisons.pop(comparison_name)
261-
# self.parent.param.trigger('comparisons')
262-
263-
264271
class PeptideFileInputControl(ControlPanel):
272+
"""
273+
This controller allows users to input .csv file (Currently only DynamX format) of 'state' peptide uptake data.
274+
Users can then choose how to correct for back-exchange and which 'state' and exposure times should be used for
275+
analysis.
276+
277+
"""
265278
header = 'Peptide Input'
266279

267280
add_button = param.Action(lambda self: self._action_add(), doc='Add File', label='Add File')
268281
clear_button = param.Action(lambda self: self._action_clear(), doc='Clear files', label='Clear Files')
269-
drop_first = param.Integer(1, bounds=(0, None))
270-
ignore_prolines = param.Boolean(True, constant=True, doc='Set to True to ignore Prolines in the sequence')
271-
load_button = param.Action(lambda self: self._action_load(), doc='Load Files', label='Load Files')
272-
282+
drop_first = param.Integer(1, bounds=(0, None), doc='Select the number of N-terminal residues to ignore.')
283+
ignore_prolines = param.Boolean(True, constant=True, doc='Prolines are ignored as they do not exchange D.')
284+
load_button = param.Action(lambda self: self._action_load(), doc='Load the selected files', label='Load Files')
273285
norm_mode = param.Selector(doc='Select method of normalization', label='Norm mode', objects=['Exp', 'Theory'])
274286
norm_state = param.Selector(doc='State used to normalize uptake', label='Norm State')
275287
norm_exposure = param.Selector(doc='Exposure used to normalize uptake', label='Norm exposure')
@@ -285,7 +297,8 @@ class PeptideFileInputControl(ControlPanel):
285297
exp_exposures = param.ListSelector(default=[], objects=[''], label='Experiment Exposures'
286298
, doc='Selected exposure time to use')
287299

288-
parse_button = param.Action(lambda self: self._action_parse(), doc='Parse', label='Parse')
300+
parse_button = param.Action(lambda self: self._action_parse(), label='Parse',
301+
doc='Parse selected peptides for further analysis and apply back-exchange correction')
289302

290303
def __init__(self, parent, **params):
291304
self.file_selectors = [pn.widgets.FileInput(accept='.csv')]
@@ -440,7 +453,7 @@ def __init__(self, parent, **params):
440453

441454
def make_list(self):
442455
lst = super(CoverageControl, self).make_list()
443-
return lst + [self.exposure_str]#, self.color_bar]
456+
return lst + [self.exposure_str]#\, self.color_bar]
444457

445458
def make_dict(self):
446459
return self.generate_widgets(index=pn.widgets.IntSlider)
@@ -452,7 +465,9 @@ def _update_cbar(self):
452465
self.color_mapper.palette = pal
453466

454467
def get_color_bar(self):
455-
"""pn.pane.Bokeh: bokeh pane with empty figure and only a color bar"""
468+
"""pn.pane.Bokeh: bokeh pane with empty figure and only a color bar
469+
Currently is buggy when added in ``make_list``
470+
"""
456471
# f5f5f5
457472
# from default value in panel css
458473
#https://github.com/holoviz/panel/blob/67bf192ea4138825ab9682c8f38bfe2d696a4e9b/panel/_styles/widgets.css
@@ -470,7 +485,7 @@ def get_color_bar(self):
470485

471486
@property
472487
def palette(self):
473-
""""""
488+
"""`obj`:tuple: Tuple of hex colors"""
474489
cmap = mpl.cm.get_cmap(self.color_map)
475490
pal = tuple(mpl.colors.to_hex(cmap(value)) for value in np.linspace(0, 1, 1024, endpoint=True))
476491
return pal
@@ -520,7 +535,7 @@ def _series_updated(self, event): #todo refactor
520535
def _update_wrap(self):
521536
y = list(itertools.islice(itertools.cycle(range(self.wrap, 0, -1)), len(self.coverage)))
522537
try:
523-
self.parent.sources['coverage'].data.update(y=y)
538+
self.parent.sources['coverage'].source.data.update(y=y)
524539
except KeyError:
525540
pass
526541

@@ -536,6 +551,12 @@ def _update_colors(self):
536551

537552

538553
class InitialGuessControl(ControlPanel):
554+
"""
555+
This controller allows users to derive initial guesses for D-exchange rate from peptide uptake data
556+
557+
"""
558+
559+
#todo remove lambda symbol although its really really funny
539560
header = 'Initial Guesses'
540561
fitting_model = param.Selector(default='Half-life (λ)', objects=['Half-life (λ)', 'Association'],
541562
doc='Choose method for determining initial guesses.')
@@ -571,9 +592,6 @@ async def _fit1_async(self):
571592
data_source = DataSource(dic, x='r_number', y='rate', tags=['mapping', 'rate'],
572593
renderer='circle', size=10)
573594

574-
# callback = partial(self.parent.param.trigger, 'sources')
575-
# self.parent.doc.add_next_tick_callback(callback)
576-
577595
#trigger plot update
578596
callback = partial(self.parent.publish_data, 'fit1', data_source)
579597
self.parent.doc.add_next_tick_callback(callback)
@@ -636,9 +654,15 @@ def _action_fit(self):
636654

637655

638656
class FitControl(ControlPanel):
657+
"""
658+
This controller allows users to execute TensorFlow fitting of the global data set.
659+
660+
Currently, repeated fitting overrides the old result.
661+
662+
"""
663+
639664
header = 'Fitting'
640665
initial_guess = param.Selector(doc='Name of dataset to use for initial guesses.')
641-
642666
c_term = param.Integer(None, doc='Residue number to which the last amino acid in the sequence corresponds.') # remove
643667
temperature = param.Number(293.15, doc='Deuterium labelling temperature in Kelvin')
644668
pH = param.Number(8., doc='Deuterium labelling pH', label='pH')
@@ -699,12 +723,10 @@ def _do_fitting(self):
699723
output_dict['color'] = np.full_like(result.output, fill_value=DEFAULT_COLORS['pfact'], dtype='<U7')
700724

701725
# output_dict[f'{var_name}_full'] = output_dict[var_name].copy()
702-
# #todo this should be moved to TFFitresults object (or shoud it?) -> DataObject class (see base) (does coloring)
703726
# output_dict[var_name][~self.parent.series.tf_cov.has_coverage] = np.nan # set no coverage sections to nan
704727

705728
#todo update when changing to fitting deltaG directly
706729
output_dict['pfact'] = 10**output_dict[var_name]
707-
# if self.fitting_type == 'Protection Factors':
708730
deltaG = constants.R * self.temperature * np.log(output_dict['pfact'])
709731
output_dict['deltaG'] = deltaG
710732

@@ -718,10 +740,14 @@ def _do_fitting(self):
718740
self.parent.param.trigger('fit_results')
719741

720742
self.parent.logger.debug('Finished TensorFlow fit')
721-
self.parent.logger.info(f'Finished fitting in {len(result.loss)} epochs')
743+
self.parent.logger.info(f'Finished fitting in {len(result.loss[0])} epochs')
722744

723745

724746
class FitResultControl(ControlPanel):
747+
"""
748+
This controller allows users to view to fit result and how it describes the uptake of every peptide.
749+
"""
750+
725751
header = 'Fit Results'
726752

727753
peptide_index = param.Number(0, bounds=(0, None),
@@ -781,10 +807,18 @@ def _update_sources(self):
781807

782808

783809
class ClassificationControl(ControlPanel):
810+
"""
811+
This controller allows users classify 'mapping' datasets and assign them colors.
812+
813+
Coloring can be either in discrete categories or as a continuous custom color map.
814+
"""
815+
784816
header = 'Classification'
785817
# format ['tag1', ('tag2a', 'tag2b') ] = tag1 OR (tag2a AND tag2b)
786-
accepted_tags = ['mapping'] #todo add 'comparison' for compare app
818+
accepted_tags = ['mapping']
787819

820+
# todo unify name for target field (target_data set)
821+
# When coupling param with the same name together there should be an option to exclude this behaviour
788822
target = param.Selector(label='Target')
789823

790824
mode = param.Selector(default='Discrete', objects=['Discrete', 'Continuous'],
@@ -1006,6 +1040,16 @@ def _update_bounds(self):
10061040

10071041

10081042
class FileExportControl(ControlPanel):
1043+
# todo check if docstring is true
1044+
"""
1045+
This controller allows users to export and download datasets.
1046+
1047+
All datasets can be exported as .txt tables.
1048+
'Mappable' datasets (with r_number column) can be exporeted as .pml pymol script, which colors protein structures
1049+
based on their 'color' column.
1050+
1051+
"""
1052+
10091053
header = "File Export"
10101054
target = param.Selector(label='Target dataset', doc='Name of the dataset to export')
10111055

@@ -1104,6 +1148,13 @@ def linear_export_callback(self):
11041148

11051149

11061150
class ProteinViewControl(ControlPanel):
1151+
"""
1152+
This controller allows users control the Protein view figure.
1153+
Structures can be specified either by RCSB ID or uploading a .pdb file.
1154+
1155+
Colors are assigned according to 'color' column of the selected dataset.
1156+
"""
1157+
11071158
header = 'Protein Viewer'
11081159
accepted_tags = ['mapping']
11091160

@@ -1151,17 +1202,18 @@ def _update_input_option(self):
11511202

11521203

11531204
class OptionsControl(ControlPanel):
1154-
header = 'Options'
1205+
"""The controller is used for various settings."""
11551206

1156-
"""panel for various options and settings"""
1207+
header = 'Options'
11571208

11581209
#todo this should be a component (mixin?) for apps who dont have these figures
1159-
link_xrange = param.Boolean(False, doc='Link the X range of the coverage figure and other linear mapping figures.')
1210+
link_xrange = param.Boolean(True, doc='Link the X range of the coverage figure and other linear mapping figures.')
11601211
log_level = param.Selector(default='DEBUG', objects=['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'OFF', 'TRACE'],
11611212
doc='Set the logging level.')
11621213

11631214
def __init__(self, parent, **param):
11641215
super(OptionsControl, self).__init__(parent, **param)
1216+
self._update_link()
11651217

11661218
@property
11671219
def enabled(self):
@@ -1203,6 +1255,8 @@ def _link(self):
12031255

12041256

12051257
class DeveloperControl(ControlPanel):
1258+
"""Controller with debugging options"""
1259+
12061260
header = 'Developer Options'
12071261
test_logging = param.Action(lambda self: self._action_test_logging())
12081262
breakpoint_btn = param.Action(lambda self: self._action_break())

pyhdx/panel/fig_panels.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def setup_hooks(self):
127127

128128

129129
class PFactFigure(ThdFigure):
130-
title = 'PFact'
130+
title = 'Protection Factors'
131131
accepted_tags = [('mapping', 'pfact')]
132132
y_label = 'Protection factor'
133133

0 commit comments

Comments
 (0)