pyKES 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {pykes-0.2.2 → pykes-0.2.3}/PKG-INFO +1 -1
- {pykes-0.2.2 → pykes-0.2.3}/pyproject.toml +1 -1
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/chunked_processing.py +101 -33
- {pykes-0.2.2 → pykes-0.2.3}/README.md +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/database/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/database/data_processing.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/database/database_experiments.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/fitting_ODE.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/pathways/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/pathways/pathways.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/pathways/transform_pathways_data.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/plotting/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/plotting/lighten_colors.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/plotting/plotting_pathways_transformed.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/plotting/plotting_tools.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/reaction_ODE.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/reaction_model.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/analysis_results_component.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/data_upload_component.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/home_component.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/results_table_component.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/time_series_component.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/config_interface.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/calculate_absorption.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/find_nearest.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/get_experiments.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/harmonize_time_series.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/make_json_serializable.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/max_rate.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/offset_correction.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/resolve_attributes.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/time_series_resampling.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/unit_handler/__init__.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/unit_handler/config.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/unit_handler/quantity.py +0 -0
- {pykes-0.2.2 → pykes-0.2.3}/src/pyKES/utilities/version_information.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyKES"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Python package for kinetic modelling of chemical reaction networks."
|
|
9
9
|
keywords = ["kinetics", "photocatalysis", "water-splitting", "reaction-networks", "ode"]
|
|
10
10
|
authors = [
|
|
@@ -9,17 +9,21 @@ messages it writes are queued and only flushed once the run yields to the
|
|
|
9
9
|
event loop, which happens after the loop has already finished. The progress
|
|
10
10
|
bar was invisible in the browser for exactly this reason.
|
|
11
11
|
|
|
12
|
-
A job started here is instead advanced by one experiment per
|
|
13
|
-
|
|
14
|
-
the
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
A job started here is instead advanced by one experiment per run of an
|
|
13
|
+
``st.fragment(run_every=...)``. The timer that drives those runs lives in the
|
|
14
|
+
*frontend*: Streamlit sends it as an auto-rerun instruction, and the browser
|
|
15
|
+
asks for the next run. Each run therefore **ends normally** and its elements
|
|
16
|
+
are delivered before the next one starts.
|
|
17
|
+
|
|
18
|
+
Ending normally is the part that matters, and it is why `st.rerun` cannot be
|
|
19
|
+
used to drive this instead. A script run that ends in `st.rerun` is followed
|
|
20
|
+
immediately by the next one, and `AppSession` clears the browser queue on every
|
|
21
|
+
``SCRIPT_STARTED`` — so a progress bar drawn by a run that then reruns is
|
|
22
|
+
discarded before it is ever sent, unless a flush happens to fall between the
|
|
23
|
+
two. On a server it usually does, because the flush runs on another thread. In
|
|
24
|
+
the browser it does not, and the bar is never seen at all. Measured in headless
|
|
25
|
+
Chrome against stlite 1.7.3: with `st.rerun` the page went from blank straight
|
|
26
|
+
to the finished state; with the timer the bar advanced step by step.
|
|
23
27
|
|
|
24
28
|
The same code path is used when running under ``streamlit run``; nothing here
|
|
25
29
|
is browser-specific.
|
|
@@ -40,6 +44,12 @@ from pyKES.database.data_processing import finalize_processing_run
|
|
|
40
44
|
# can report them after the final rerun has torn the job down.
|
|
41
45
|
JOB_RESULTS_KEY_TEMPLATE = "{job_key}_results"
|
|
42
46
|
|
|
47
|
+
# How often the frontend asks for the next step of a running job. It only paces
|
|
48
|
+
# the hand-over between steps — the step itself takes as long as it takes — so
|
|
49
|
+
# it is set well below the duration of any real experiment and costs one round
|
|
50
|
+
# trip per step.
|
|
51
|
+
JOB_STEP_INTERVAL_SECONDS = 0.1
|
|
52
|
+
|
|
43
53
|
|
|
44
54
|
def job_results_key(job_key: str) -> str:
|
|
45
55
|
"""
|
|
@@ -96,7 +106,6 @@ def start_chunked_job(job_key: str,
|
|
|
96
106
|
'experiment_names': experiment_names,
|
|
97
107
|
'completed': 0,
|
|
98
108
|
'results': [],
|
|
99
|
-
'painted': False,
|
|
100
109
|
'staging_directory': staging_directory,
|
|
101
110
|
'context': context,
|
|
102
111
|
}
|
|
@@ -138,19 +147,37 @@ def collect_job_results(job_key: str) -> Optional[list]:
|
|
|
138
147
|
return st.session_state.pop(job_results_key(job_key), None)
|
|
139
148
|
|
|
140
149
|
|
|
150
|
+
def job_is_complete(job: dict) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Report whether every experiment of a job has been processed.
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
job : dict
|
|
157
|
+
Job state.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
bool
|
|
162
|
+
True once no experiment is left.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
return job['completed'] == len(job['experiment_names'])
|
|
166
|
+
|
|
167
|
+
|
|
141
168
|
def paint_job_progress(job: dict) -> None:
|
|
142
169
|
"""
|
|
143
170
|
Draw the progress bar for the experiment that is about to be processed.
|
|
144
171
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
never show the first one — which is the whole bug this module exists for.
|
|
172
|
+
Called at the end of every step, so the run that delivers this bar is over
|
|
173
|
+
before the experiment it names occupies the worker. The bar therefore
|
|
174
|
+
stands on screen for exactly as long as that experiment takes.
|
|
149
175
|
|
|
150
176
|
Parameters
|
|
151
177
|
----------
|
|
152
178
|
job : dict
|
|
153
|
-
Job state
|
|
179
|
+
Job state. Must not be complete — the caller finishes the job first, so
|
|
180
|
+
``completed`` always indexes an experiment here.
|
|
154
181
|
|
|
155
182
|
Returns
|
|
156
183
|
-------
|
|
@@ -165,8 +192,6 @@ def paint_job_progress(job: dict) -> None:
|
|
|
165
192
|
text = f"Processing experiment {completed + 1}/{total}: {job['experiment_names'][completed]}",
|
|
166
193
|
)
|
|
167
194
|
|
|
168
|
-
job['painted'] = True
|
|
169
|
-
|
|
170
195
|
|
|
171
196
|
def run_job_step(job: dict, step_function: Callable[..., dict]) -> None:
|
|
172
197
|
"""
|
|
@@ -175,8 +200,7 @@ def run_job_step(job: dict, step_function: Callable[..., dict]) -> None:
|
|
|
175
200
|
Parameters
|
|
176
201
|
----------
|
|
177
202
|
job : dict
|
|
178
|
-
Job state; ``results
|
|
179
|
-
place.
|
|
203
|
+
Job state; ``results`` and ``completed`` are updated in place.
|
|
180
204
|
step_function : callable
|
|
181
205
|
``(experiment_name, **context) -> result_dict``, e.g.
|
|
182
206
|
`pyKES.database.data_processing.ingest_experiment`.
|
|
@@ -190,7 +214,6 @@ def run_job_step(job: dict, step_function: Callable[..., dict]) -> None:
|
|
|
190
214
|
|
|
191
215
|
job['results'].append(step_function(experiment_name, **job['context']))
|
|
192
216
|
job['completed'] += 1
|
|
193
|
-
job['painted'] = False
|
|
194
217
|
|
|
195
218
|
|
|
196
219
|
def finish_chunked_job(job_key: str, job: dict) -> None:
|
|
@@ -222,12 +245,40 @@ def finish_chunked_job(job_key: str, job: dict) -> None:
|
|
|
222
245
|
del st.session_state[job_key]
|
|
223
246
|
|
|
224
247
|
|
|
225
|
-
|
|
248
|
+
@st.fragment(run_every=JOB_STEP_INTERVAL_SECONDS)
|
|
249
|
+
def render_job_progress(job_key: str) -> None:
|
|
226
250
|
"""
|
|
227
|
-
|
|
251
|
+
Draw the progress bar of a running job. Draws nothing else, ever.
|
|
228
252
|
|
|
229
|
-
|
|
230
|
-
|
|
253
|
+
Deliberately a separate fragment from `advance_job`: a fragment rerun
|
|
254
|
+
clears the deltas of the fragments *in that run* and preserves everyone
|
|
255
|
+
else's, so a bar drawn here survives every step the worker takes. Drawn
|
|
256
|
+
from inside the worker instead, it was wiped by the worker's own next tick
|
|
257
|
+
before it was ever sent, and the browser showed the first experiment for
|
|
258
|
+
the whole run.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
job_key : str
|
|
263
|
+
Session-state key identifying the job.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
None : None
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
job = active_job(job_key)
|
|
271
|
+
|
|
272
|
+
if job is None or job_is_complete(job):
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
paint_job_progress(job)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@st.fragment(run_every=JOB_STEP_INTERVAL_SECONDS)
|
|
279
|
+
def advance_job(job_key: str, step_function: Callable[..., dict]) -> None:
|
|
280
|
+
"""
|
|
281
|
+
Process one experiment per timer tick. Writes nothing to the page.
|
|
231
282
|
|
|
232
283
|
Parameters
|
|
233
284
|
----------
|
|
@@ -246,17 +297,34 @@ def render_chunked_job(job_key: str, step_function: Callable[..., dict]) -> None
|
|
|
246
297
|
if job is None:
|
|
247
298
|
return
|
|
248
299
|
|
|
249
|
-
|
|
250
|
-
if job['completed'] == len(job['experiment_names']):
|
|
300
|
+
if job_is_complete(job):
|
|
251
301
|
finish_chunked_job(job_key, job)
|
|
302
|
+
|
|
303
|
+
# App-scoped: the page re-renders once without either fragment, so the
|
|
304
|
+
# timers stop and the sections skipped during the job come back.
|
|
252
305
|
st.rerun()
|
|
253
306
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
307
|
+
run_job_step(job, step_function)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def render_chunked_job(job_key: str, step_function: Callable[..., dict]) -> None:
|
|
311
|
+
"""
|
|
312
|
+
Run a job to completion, one experiment per timer tick, showing progress.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
job_key : str
|
|
317
|
+
Session-state key identifying the job.
|
|
318
|
+
step_function : callable
|
|
319
|
+
``(experiment_name, **context) -> result_dict``.
|
|
320
|
+
|
|
321
|
+
Returns
|
|
322
|
+
-------
|
|
323
|
+
None : None
|
|
324
|
+
"""
|
|
258
325
|
|
|
259
|
-
|
|
326
|
+
render_job_progress(job_key)
|
|
327
|
+
advance_job(job_key, step_function)
|
|
260
328
|
|
|
261
329
|
|
|
262
330
|
def any_active_job(job_keys: list) -> bool:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pykes-0.2.2 → pykes-0.2.3}/src/pyKES/streamlit_app/components/analysis_results_component.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|