apmtools 0.2.25__tar.gz → 0.2.26__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.
- {apmtools-0.2.25 → apmtools-0.2.26}/PKG-INFO +1 -1
- {apmtools-0.2.25 → apmtools-0.2.26}/pyproject.toml +1 -1
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/shiny/summary.py +85 -78
- {apmtools-0.2.25 → apmtools-0.2.26}/.gitignore +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/LICENSE +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/README.md +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/__init__.py +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/classes.py +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/data_processing.py +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/functions.py +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/legacy.py +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/shiny/__init__.py +0 -0
- {apmtools-0.2.25 → apmtools-0.2.26}/src/apmtools/units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: apmtools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.026
|
|
4
4
|
Summary: A collection of tools for processing air pollution monitoring data
|
|
5
5
|
Project-URL: Homepage, https://github.com/federlorenz/apmtools
|
|
6
6
|
Author-email: Federico Lorenzetti <lorenzetti.federico@gmail.com>
|
|
@@ -449,9 +449,26 @@ def create_summary_app(
|
|
|
449
449
|
subset_columns = columns
|
|
450
450
|
|
|
451
451
|
@module.ui
|
|
452
|
-
def plot_tab_ui():
|
|
452
|
+
def plot_tab_ui(initial_filters=None):
|
|
453
|
+
initial_filters = initial_filters or {}
|
|
453
454
|
return ui.div(
|
|
454
455
|
ui.layout_columns(
|
|
456
|
+
ui.card(ui.card_header("Plot filters"),
|
|
457
|
+
ui.input_selectize("subset_columns", "Subset columns",
|
|
458
|
+
choices={
|
|
459
|
+
c: c for c in subset_columns},
|
|
460
|
+
selected=initial_filters.get("columns", []), multiple=True),
|
|
461
|
+
ui.output_ui("subset_filters"),
|
|
462
|
+
ui.hr(),
|
|
463
|
+
ui.input_text("datetime_start", "Start datetime",
|
|
464
|
+
value=initial_filters.get("start", ""),
|
|
465
|
+
placeholder="YYYY-MM-DD or YYYY-MM-DD HH:MM:SS"),
|
|
466
|
+
ui.input_text("datetime_end", "End datetime",
|
|
467
|
+
value=initial_filters.get("end", ""),
|
|
468
|
+
placeholder="YYYY-MM-DD or YYYY-MM-DD HH:MM:SS"),
|
|
469
|
+
ui.help_text(
|
|
470
|
+
"Filters affect this plot only. A new plot inherits these selections."),
|
|
471
|
+
),
|
|
455
472
|
ui.card(ui.card_header("Plot settings"),
|
|
456
473
|
ui.input_select("plot_type", "Plot type",
|
|
457
474
|
choices=PLOT_TYPES, selected=plot_type),
|
|
@@ -478,14 +495,64 @@ def create_summary_app(
|
|
|
478
495
|
),
|
|
479
496
|
ui.input_text("title", "Plot title", value=title),
|
|
480
497
|
),
|
|
481
|
-
ui.
|
|
482
|
-
"
|
|
483
|
-
|
|
498
|
+
ui.div(
|
|
499
|
+
ui.card(ui.card_header("Plot"),
|
|
500
|
+
ui.div(output_widget("plot", height="100%", width="100%"),
|
|
501
|
+
class_="summary-plot-container")),
|
|
502
|
+
ui.card(ui.card_header("Dataset used for plotting"),
|
|
503
|
+
ui.output_data_frame("plot_table")),
|
|
504
|
+
),
|
|
505
|
+
col_widths=(2, 2, 8),
|
|
484
506
|
),
|
|
485
507
|
)
|
|
486
508
|
|
|
487
509
|
@module.server
|
|
488
|
-
def plot_tab_server(input, output, session,
|
|
510
|
+
def plot_tab_server(input, output, session, initial_filters=None):
|
|
511
|
+
initial_filters = initial_filters or {}
|
|
512
|
+
|
|
513
|
+
@render.ui
|
|
514
|
+
def subset_filters():
|
|
515
|
+
controls = []
|
|
516
|
+
for i, name in enumerate(list(input.subset_columns())):
|
|
517
|
+
if name not in column_map:
|
|
518
|
+
continue
|
|
519
|
+
choices, _ = _subset_choices(summary, column_map[name])
|
|
520
|
+
controls.append(ui.input_selectize(
|
|
521
|
+
f"subset_values_{i}", f"{name} values", choices=choices,
|
|
522
|
+
selected=initial_filters.get(
|
|
523
|
+
"values", {}).get(name, []),
|
|
524
|
+
multiple=True,
|
|
525
|
+
))
|
|
526
|
+
return ui.div(*controls) if controls else ui.help_text("Select columns to filter this plot.")
|
|
527
|
+
|
|
528
|
+
@reactive.calc
|
|
529
|
+
def filter_state():
|
|
530
|
+
names = list(input.subset_columns())
|
|
531
|
+
values = {}
|
|
532
|
+
for i, name in enumerate(names):
|
|
533
|
+
if name in column_map:
|
|
534
|
+
values[name] = list(input[f"subset_values_{i}"]())
|
|
535
|
+
return {"columns": names, "values": values,
|
|
536
|
+
"start": input.datetime_start().strip(),
|
|
537
|
+
"end": input.datetime_end().strip()}
|
|
538
|
+
|
|
539
|
+
@reactive.calc
|
|
540
|
+
def filtered_summary():
|
|
541
|
+
state = filter_state()
|
|
542
|
+
filters = {}
|
|
543
|
+
for name, keys in state["values"].items():
|
|
544
|
+
_, mapping = _subset_choices(summary, column_map[name])
|
|
545
|
+
selected = [mapping[key] for key in keys if key in mapping]
|
|
546
|
+
if selected:
|
|
547
|
+
filters[column_map[name]] = selected
|
|
548
|
+
return _apply_filters(summary, filters, state["start"], state["end"])
|
|
549
|
+
|
|
550
|
+
@render.data_frame
|
|
551
|
+
def plot_table():
|
|
552
|
+
df = selected_data()
|
|
553
|
+
return render.DataGrid(pd.DataFrame(df).reset_index(drop=True),
|
|
554
|
+
filters=True, height="420px")
|
|
555
|
+
|
|
489
556
|
@render.ui
|
|
490
557
|
def x_control():
|
|
491
558
|
kind = input.plot_type()
|
|
@@ -788,21 +855,9 @@ def create_summary_app(
|
|
|
788
855
|
)
|
|
789
856
|
return fig
|
|
790
857
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
ui.input_selectize("subset_columns", "Subset columns", choices={
|
|
795
|
-
c: c for c in subset_columns}, selected=[], multiple=True),
|
|
796
|
-
ui.output_ui("subset_filters"),
|
|
797
|
-
ui.hr(),
|
|
798
|
-
ui.h5("Datetime filter"),
|
|
799
|
-
ui.input_text("datetime_start", "Start datetime", value="",
|
|
800
|
-
placeholder="YYYY-MM-DD or YYYY-MM-DD HH:MM:SS"),
|
|
801
|
-
ui.input_text("datetime_end", "End datetime", value="",
|
|
802
|
-
placeholder="YYYY-MM-DD or YYYY-MM-DD HH:MM:SS"),
|
|
803
|
-
ui.help_text(
|
|
804
|
-
"Global filters update all plots. Plot controls are independent for each tab."),
|
|
805
|
-
),
|
|
858
|
+
return filter_state
|
|
859
|
+
|
|
860
|
+
app_ui = ui.page_fluid(
|
|
806
861
|
ui.tags.style("""
|
|
807
862
|
.container, .container-fluid {max-width: none !important;}
|
|
808
863
|
.summary-plot-container {width:100%; height:auto; aspect-ratio: 2 / 1; min-width:0; position:relative;}
|
|
@@ -850,69 +905,15 @@ def create_summary_app(
|
|
|
850
905
|
class_="d-flex gap-2 mb-3",
|
|
851
906
|
),
|
|
852
907
|
ui.navset_tab(ui.nav_panel("Plot 1", plot_tab_ui(
|
|
853
|
-
"plot_1"), value="plot_1"), id="plot_tabs"),
|
|
908
|
+
"plot_1", {}), value="plot_1"), id="plot_tabs"),
|
|
854
909
|
),
|
|
855
910
|
title="Summary plots",
|
|
856
911
|
)
|
|
857
912
|
|
|
858
913
|
def server(input, output, session):
|
|
859
|
-
@render.ui
|
|
860
|
-
def subset_filters():
|
|
861
|
-
"""Render one value selector for every selected subset column."""
|
|
862
|
-
selected_columns = list(input.subset_columns())
|
|
863
|
-
if not selected_columns:
|
|
864
|
-
return ui.help_text(
|
|
865
|
-
"Select one or more columns above to choose their values."
|
|
866
|
-
)
|
|
867
|
-
|
|
868
|
-
controls = []
|
|
869
|
-
for i, selected_column in enumerate(selected_columns):
|
|
870
|
-
if selected_column not in column_map:
|
|
871
|
-
continue
|
|
872
|
-
choices, _ = _subset_choices(
|
|
873
|
-
summary, column_map[selected_column])
|
|
874
|
-
input_id = f"subset_values_{i}"
|
|
875
|
-
controls.append(
|
|
876
|
-
ui.input_selectize(
|
|
877
|
-
input_id,
|
|
878
|
-
f"{selected_column} values",
|
|
879
|
-
choices=choices,
|
|
880
|
-
selected=[],
|
|
881
|
-
multiple=True,
|
|
882
|
-
options={
|
|
883
|
-
"placeholder": f"Select {selected_column} values"},
|
|
884
|
-
)
|
|
885
|
-
)
|
|
886
|
-
return ui.div(*controls)
|
|
887
|
-
|
|
888
|
-
@reactive.calc
|
|
889
|
-
def filtered_summary():
|
|
890
|
-
selected_columns = list(input.subset_columns())
|
|
891
|
-
subset_filters = {}
|
|
892
|
-
|
|
893
|
-
for i, selected_column in enumerate(selected_columns):
|
|
894
|
-
if selected_column not in column_map:
|
|
895
|
-
continue
|
|
896
|
-
selected_keys = list(input[f"subset_values_{i}"]())
|
|
897
|
-
if not selected_keys:
|
|
898
|
-
# An explicitly selected column with no selected values
|
|
899
|
-
# should not remove all data; it simply contributes no
|
|
900
|
-
# filtering condition until values are chosen.
|
|
901
|
-
continue
|
|
902
|
-
_, value_map = _subset_choices(
|
|
903
|
-
summary, column_map[selected_column])
|
|
904
|
-
values = [value_map[key]
|
|
905
|
-
for key in selected_keys if key in value_map]
|
|
906
|
-
if values:
|
|
907
|
-
subset_filters[column_map[selected_column]] = values
|
|
908
|
-
|
|
909
|
-
start = input.datetime_start().strip()
|
|
910
|
-
end = input.datetime_end().strip()
|
|
911
|
-
return _apply_filters(summary, subset_filters, start, end)
|
|
912
|
-
|
|
913
914
|
next_tab = reactive.value(1)
|
|
914
915
|
active_tabs = reactive.value(("plot_1",))
|
|
915
|
-
plot_tab_server("plot_1",
|
|
916
|
+
tab_filters = {"plot_1": plot_tab_server("plot_1", {})}
|
|
916
917
|
|
|
917
918
|
@reactive.effect
|
|
918
919
|
@reactive.event(input.add_tab)
|
|
@@ -921,10 +922,16 @@ def create_summary_app(
|
|
|
921
922
|
next_tab.set(number)
|
|
922
923
|
tab_id = f"plot_{number}"
|
|
923
924
|
# Register module before inserting its UI.
|
|
924
|
-
|
|
925
|
+
selected = input.plot_tabs()
|
|
926
|
+
# Snapshot the selected plot's filters when the new plot is created.
|
|
927
|
+
inherited = dict(tab_filters[selected]
|
|
928
|
+
()) if selected in tab_filters else {}
|
|
929
|
+
inherited["values"] = {
|
|
930
|
+
k: list(v) for k, v in inherited.get("values", {}).items()}
|
|
931
|
+
tab_filters[tab_id] = plot_tab_server(tab_id, inherited)
|
|
925
932
|
active_tabs.set((*active_tabs.get(), tab_id))
|
|
926
933
|
ui.insert_nav_panel("plot_tabs", ui.nav_panel(
|
|
927
|
-
f"Plot {number}", plot_tab_ui(tab_id), value=tab_id), select=True)
|
|
934
|
+
f"Plot {number}", plot_tab_ui(tab_id, inherited), value=tab_id), select=True)
|
|
928
935
|
|
|
929
936
|
@reactive.effect
|
|
930
937
|
@reactive.event(input.close_tab)
|
|
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
|