bindmc 0.1.0__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.
Files changed (37) hide show
  1. bindmc/main.py +67 -0
  2. bindmc/webgui/__init__.py +0 -0
  3. bindmc/webgui/app.py +54 -0
  4. bindmc/webgui/classes/BindingConstant.py +23 -0
  5. bindmc/webgui/classes/ChemicalShiftParam.py +40 -0
  6. bindmc/webgui/classes/Component.py +111 -0
  7. bindmc/webgui/classes/ExptData.py +485 -0
  8. bindmc/webgui/classes/ExptDataType.py +92 -0
  9. bindmc/webgui/classes/FitResult.py +173 -0
  10. bindmc/webgui/classes/MCMCSim.py +232 -0
  11. bindmc/webgui/classes/Model.py +86 -0
  12. bindmc/webgui/classes/RawData.py +36 -0
  13. bindmc/webgui/classes/Simulation.py +104 -0
  14. bindmc/webgui/classes/UIBindings.py +19 -0
  15. bindmc/webgui/classes/__init__.py +28 -0
  16. bindmc/webgui/components/__init__.py +29 -0
  17. bindmc/webgui/components/base.py +24 -0
  18. bindmc/webgui/components/bayes.py +689 -0
  19. bindmc/webgui/components/bayes_priors.py +351 -0
  20. bindmc/webgui/components/binding_model.py +330 -0
  21. bindmc/webgui/components/body.py +276 -0
  22. bindmc/webgui/components/data_gen.py +419 -0
  23. bindmc/webgui/components/data_import.py +450 -0
  24. bindmc/webgui/components/data_model.py +609 -0
  25. bindmc/webgui/components/fitting.py +886 -0
  26. bindmc/webgui/components/graph.py +649 -0
  27. bindmc/webgui/components/header.py +124 -0
  28. bindmc/webgui/components/simulation.py +385 -0
  29. bindmc/webgui/export/__init__.py +0 -0
  30. bindmc/webgui/export/notebook_exporter.py +727 -0
  31. bindmc/webgui/state/__init__.py +1 -0
  32. bindmc/webgui/state/statemanager.py +2043 -0
  33. bindmc/webgui/utils.py +322 -0
  34. bindmc-0.1.0.dist-info/METADATA +22 -0
  35. bindmc-0.1.0.dist-info/RECORD +37 -0
  36. bindmc-0.1.0.dist-info/WHEEL +5 -0
  37. bindmc-0.1.0.dist-info/top_level.txt +1 -0
bindmc/main.py ADDED
@@ -0,0 +1,67 @@
1
+ #to build: rm -fr build && rm -fr dist && nicegui-pack --onefile --add-data "C:\Users\mpeeks\miniforge3\envs\binding-nicegui\Lib\site-packages\arviz\static;arviz/static" --add-data "C:\Users\mpeeks\miniforge3\envs\binding-nicegui\Lib\site-packages\arviz\data;arviz/data" --add-data "C:\Users\mpeeks\miniforge3\envs\binding-nicegui\Lib\site-packages\latex2mathml\unimathsymbols.txt;latex2mathml" main.py
2
+
3
+ # on windows:
4
+ # nicegui-pack main.py --no-build
5
+
6
+ # macOS packaging support
7
+ from multiprocessing import freeze_support # noqa
8
+ freeze_support() # noqa
9
+
10
+ # hidden import for pyinstaller
11
+ import matplotlib
12
+ matplotlib.use('module://matplotlib.backends.backend_svg')
13
+ import sys
14
+ from nicegui import native,ui,app
15
+ from webgui.app import BindToolsServer
16
+ import logging
17
+ import nicegui
18
+ from packaging.version import InvalidVersion, Version
19
+ from pathlib import Path
20
+ from platformdirs import user_data_dir
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ try:
25
+ nicegui_version = Version(nicegui.__version__)
26
+ except InvalidVersion:
27
+ logger.warning(
28
+ "Could not parse NiceGUI version '%s'; continuing, but NiceGUI >= 3 is required.",
29
+ nicegui.__version__,
30
+ )
31
+ else:
32
+ if nicegui_version.is_prerelease or nicegui_version.is_devrelease:
33
+ logger.warning(
34
+ "NiceGUI %s is a pre-release/dev build; recommended to install the latest stable 3.x.",
35
+ nicegui.__version__,
36
+ )
37
+ elif nicegui_version.major < 3:
38
+ raise RuntimeError(f"NiceGUI >= 3 is required; found {nicegui.__version__}")
39
+
40
+
41
+ app.native.settings['ALLOW_DOWNLOADS'] = True
42
+
43
+ #logging.basicConfig(level=logging.INFO, filename='bindtools.log')
44
+ logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(message)s')
45
+
46
+ logger.info("Starting BindTools NiceGUI server...")
47
+ BindToolsServer()
48
+
49
+ # Set DEV based on whether running from PyInstaller bundle
50
+ DEV = not getattr(sys, 'frozen', False)
51
+
52
+ if DEV:
53
+ native_mode=False
54
+ reload=True
55
+ else:
56
+ native_mode=True
57
+ reload=False
58
+
59
+
60
+ # make a sensible storage path for native mode
61
+ storage_path = Path(user_data_dir(appname="BindTools", appauthor=False))
62
+ storage_path.mkdir(parents=True, exist_ok=True)
63
+
64
+ # Redirect native window persistence data away from default paths
65
+ app.native.start_args['storage_path'] = str(storage_path)
66
+
67
+ ui.run(title='BindTools', reload=reload, native=native_mode, port=native.find_open_port(), storage_secret='bindtools_secret')
File without changes
bindmc/webgui/app.py ADDED
@@ -0,0 +1,54 @@
1
+ from nicegui import ui
2
+
3
+ from .components import (
4
+ BindToolsHeader,
5
+ Body
6
+ )
7
+ from .state.statemanager import StateManager
8
+
9
+ class BindToolsServer:
10
+
11
+ def __init__(self):
12
+
13
+ self.state_manager: StateManager = StateManager(load_prior_state=False) # Initialize state_manager attribute
14
+ self.sm: StateManager =self.state_manager
15
+ self.components = {}
16
+ self.body_components={}
17
+ self.tabs = {}
18
+ self.setup_routes()
19
+
20
+
21
+ def setup_routes(self):
22
+ """Set up the application routes and UI components."""
23
+
24
+ @ui.page("/")
25
+
26
+ def index():
27
+ self.state_manager = StateManager()
28
+ self.sm = self.state_manager # alias
29
+ self._generate_header()
30
+ self._generate_body()
31
+ self.body_components = self.components["body"].components
32
+ self._load_prior_state()
33
+
34
+
35
+
36
+ def _load_prior_state(self):
37
+ # if simulations have been run already, populate the graph
38
+ if len(self.sm.simulations) > 0:
39
+ self.body_components["simulation"].graph.load_simulations_data()
40
+ if self.sm.active_expt_data_id is not None and self.sm.active_expt_data.data is not None and not self.sm.active_expt_data.data.empty:
41
+ self.sm.notify_listeners("data_imported")
42
+ if len(self.sm.fits) > 0:
43
+ self.sm.notify_listeners("fits_loaded")
44
+ # self.components["fit_results"].sync_graphs()
45
+ # self.components["fit_results"].generate_delete_fit_dropdown()
46
+
47
+
48
+ def _generate_header(self):
49
+ ui.colors(primary='#000000', secondary='grey-5', accent='blue-grey-5')
50
+
51
+ self.components["header"] = BindToolsHeader(state_manager=self.state_manager)
52
+
53
+ def _generate_body(self):
54
+ self.components["body"]= Body(self.state_manager)
@@ -0,0 +1,23 @@
1
+ import uuid
2
+ from dataclasses import asdict, dataclass, field, InitVar
3
+ from typing import Optional,Any
4
+ import unicodedata
5
+ from nicegui import binding
6
+ import numpy as np
7
+ import pandas as pd
8
+ from lmfit import Parameter as LMFitParameter
9
+
10
+ @dataclass
11
+ class BindingConstant:
12
+ species: str = ""
13
+ logK: Optional[float] = None
14
+ vary: bool = False
15
+ isComp: bool = False
16
+ min: Optional[float] = None
17
+ max: Optional[float] = None
18
+
19
+ @property
20
+ def name(self) -> str:
21
+ return self.species
22
+
23
+
@@ -0,0 +1,40 @@
1
+
2
+ import uuid
3
+ from dataclasses import asdict, dataclass, field, InitVar
4
+ from typing import Optional,Any
5
+ import unicodedata
6
+ from nicegui import binding
7
+ import numpy as np
8
+ import pandas as pd
9
+ from lmfit import Parameter as LMFitParameter
10
+
11
+ @dataclass
12
+ class ChemicalShiftParam():
13
+ """Data class to represent a chemical shift parameter."""
14
+ species: str = ""
15
+ col: str | None = None # raw_data column this resonance/shift belongs to (fast-exchange)
16
+ value: float | None = None
17
+ fixed: bool = False # Whether the parameter is fixed or not
18
+ _min: float |None = None # Minimum value for the parameter
19
+ _max: float |None = None # Maximum value for the parameter
20
+
21
+ init_min: InitVar[Optional[float]] = None # Initial minimum value for the parameter
22
+ init_max: InitVar[Optional[float]] = None # Initial maximum value for
23
+
24
+ def __post_init__(self, init_min: Optional[float] = None, init_max: Optional[float] = None) -> None:
25
+ # if not self.fixed and not isinstance(init_min,float):
26
+ # raise ValueError("Variable parameters must have a valid initial minimum value.")
27
+ # if not self.fixed and not isinstance(init_max,float):
28
+ # raise ValueError("Variable parameters must have a valid initial maximum value.")
29
+ if init_min is not None and init_max is not None and self.value is not None:
30
+ if init_min >= init_max or init_min>self.value or init_max<self.value:
31
+ raise ValueError("Initial min and max values must be valid and lesser/greater than the value.")
32
+ if init_min is None and self._min is not None:
33
+ return # No need to set if already defined
34
+ if init_max is None and self._max is not None:
35
+ return # No need to set if already defined
36
+ self._min = init_min
37
+ self._max = init_max
38
+ # else:
39
+ # raise ValueError("Initial min and max values must be provided for variable parameters.")
40
+
@@ -0,0 +1,111 @@
1
+ import uuid
2
+ from dataclasses import asdict, dataclass, field, InitVar
3
+ from typing import Optional,Any
4
+ import unicodedata
5
+ from nicegui import binding
6
+ import numpy as np
7
+ import pandas as pd
8
+ from lmfit import Parameter as LMFitParameter
9
+
10
+ @dataclass
11
+ class Component:
12
+ name: str = ""
13
+ start_conc: float|None = 0
14
+ end_conc: float|None = 0
15
+ constant: bool = False
16
+ _start_units: str = "mM"
17
+ _end_units: str = "mM"
18
+ spacing: str = "lin" # 'lin' or 'log', default is linear spacing
19
+
20
+ UNIT_CONVERSIONS = {
21
+ "M": 1,
22
+ "mM": 1e3,
23
+ "μM": 1e6,
24
+ "µM": 1e6,
25
+ "uM": 1e6, # support ASCII 'u'
26
+ "nM": 1e9,
27
+ "pM": 1e12,
28
+ }
29
+
30
+ @property
31
+ def start_conc_nice(self) -> float | None:
32
+ """Get start concentration in the specified units (user-friendly)."""
33
+ factor = self.UNIT_CONVERSIONS.get(self.start_units, 1)
34
+ return self.start_conc * factor if self.start_conc is not None else None
35
+
36
+ @start_conc_nice.setter
37
+ def start_conc_nice(self, value: float):
38
+ """Set start concentration from user-friendly units to base units (M)."""
39
+ factor = self.UNIT_CONVERSIONS.get(self.start_units, 1)
40
+ self.start_conc = value / factor if value is not None else None
41
+
42
+ @property
43
+ def end_conc_nice(self) -> float|None:
44
+ """Get end concentration in the specified units (user-friendly)."""
45
+ factor = self.UNIT_CONVERSIONS.get(self.end_units, 1)
46
+ return self.end_conc * factor if self.end_conc is not None else None
47
+
48
+ @end_conc_nice.setter
49
+ def end_conc_nice(self, value: float):
50
+ """Set end concentration from user-friendly units to base units (M)."""
51
+ factor = self.UNIT_CONVERSIONS.get(self.end_units, 1)
52
+ self.end_conc = value / factor if value is not None else None
53
+
54
+ @property
55
+ def start_units(self) -> str:
56
+ """Get the start concentration units."""
57
+ return self._start_units
58
+
59
+ @start_units.setter
60
+ def start_units(self, value: str):
61
+ """Set the start concentration units and recalculate start_conc."""
62
+ oldunits = self._start_units
63
+ convfactor = self.UNIT_CONVERSIONS.get(value, 1) / self.UNIT_CONVERSIONS.get(
64
+ oldunits, 1
65
+ )
66
+ self._start_units = value
67
+
68
+ self.start_conc = (
69
+ self.start_conc / convfactor if self.start_conc is not None else None
70
+ )
71
+
72
+ @property
73
+ def end_units(self) -> str:
74
+ """Get the end concentration units."""
75
+ return self._end_units
76
+
77
+ @end_units.setter
78
+ def end_units(self, value: str):
79
+ """Set the end concentration units and recalculate end."""
80
+ oldunits = self._end_units
81
+ convfactor = self.UNIT_CONVERSIONS.get(value, 1) / self.UNIT_CONVERSIONS.get(
82
+ oldunits, 1
83
+ )
84
+ self._end_units = value
85
+
86
+ self.end_conc = (
87
+ self.end_conc / convfactor if self.end_conc is not None else None
88
+ )
89
+
90
+ # """Class to represent a component in the simulation."""
91
+ # def __init__(self, name, start_conc=None, end_conc=None, constant=False, start_unit='mM', end_unit='mM'):
92
+
93
+ # self.name = name
94
+ # self.start_conc = start_conc
95
+ # self.end_conc = end_conc
96
+ # self.constant = constant
97
+ # self.start_unit = start_unit
98
+ # self.end_unit = end_unit
99
+
100
+ # def to_dict(self):
101
+ # """Convert Component to a dictionary."""
102
+ # return {
103
+ # 'name': self.name,
104
+ # 'start_conc': self.start_conc,
105
+ # 'end_conc': self.end_conc,
106
+ # 'constant': self.constant,
107
+ # 'start_unit': self.start_unit,
108
+ # 'end_unit': self.end_unit
109
+ # }
110
+
111
+