-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
308 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
Remove the margin from the body so the ProtoApp fills the page | ||
*/ | ||
body { | ||
margin: 0px 0px 0px 0px; | ||
} | ||
|
||
/* | ||
Window buttons | ||
These must be configured individually due to Panel widgets | ||
creating them as different classes | ||
button.bk.bk-btn.bk-btn-default is the default button widget | ||
a.bk-btn.bk-btn-default is the default download button widget | ||
*/ | ||
button.bk.bk-btn.bk-btn-default { | ||
color: #FFFFFF; | ||
background-color: #AD9C70; | ||
border: 0px; | ||
} | ||
a.bk-btn.bk-btn-default { | ||
color: #FFFFFF; | ||
background-color: #AD9C70; | ||
border: 0px; | ||
} | ||
button.bk.bk-btn.bk-btn-default:hover { | ||
color: #FFFFFF; | ||
background-color: #9b8d69; | ||
} | ||
a.bk-btn.bk-btn-default:hover { | ||
color: #FFFFFF; | ||
background-color: #9b8d69; | ||
} | ||
|
||
/* | ||
Metadata input dropdown | ||
*/ | ||
.bk.card { | ||
border-color: #FFFFFF; | ||
} | ||
.bk.card-header { | ||
background-color: #AD9C70; | ||
} | ||
.bk.card-header:hover { | ||
background-color: #9b8d69; | ||
} | ||
.bk.card-button { | ||
color: #FFFFFF; | ||
} | ||
.bk.card-header-row { | ||
color: #FFFFFF; | ||
} | ||
.bk.bk-clearfix { | ||
font-size: 12px; | ||
font-weight: 400; | ||
} | ||
textarea { | ||
resize: none; | ||
} | ||
|
||
/* | ||
Custom CSS classes for multiple widgets | ||
They must all start with .bk. followed by the CSS class name | ||
.bk.custom_css_class_name | ||
*/ | ||
.bk.custom_header { | ||
text-align: center; | ||
} | ||
.bk.bgs_blue_background { | ||
color: #FFFFFF; | ||
background-color: #002E40; | ||
} | ||
.bk.left_sidebar_divider { | ||
background-color: #AD9C70; | ||
} | ||
.bk.right_sidebar { | ||
background-color: #f8f8f8; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
import datetime | ||
import itertools | ||
import json | ||
import logging | ||
import param | ||
|
||
import pandas as pd | ||
import panel as pn | ||
|
||
from copy import deepcopy | ||
from io import StringIO, BytesIO | ||
|
||
import bokeh.plotting.figure | ||
from bokeh.palettes import Category20 | ||
from bokeh.models import HoverTool, Range1d | ||
from bokeh.models.widgets.tables import BooleanFormatter | ||
|
||
# Set up the CSS | ||
pn.extension( | ||
sizing_mode="stretch_width", | ||
css_files=["app/hazard.css"], | ||
notifications=True, | ||
) | ||
|
||
logger = logging.getLogger('hazards') | ||
logger.setLevel(logging.DEBUG) | ||
logging.basicConfig(level=logging.DEBUG, | ||
format="%(asctime)s %(name)s %(message)s", | ||
force=True) | ||
|
||
# Suppress debug messages from other packages | ||
for package in ('bokeh', 'matplotlib', 'asyncio', 'MARKDOWN'): | ||
logging.getLogger(package).setLevel(logging.INFO) | ||
|
||
|
||
class tomrapApp(param.Parameterized): | ||
""" | ||
The tomrap Application is created using a class using parameterization. | ||
""" | ||
# Parameterized values allow for callbacks to be made on input fields | ||
pressures_file_input = param.Parameter( | ||
doc=""" | ||
The file input widget for the pressures CSV file. | ||
""", | ||
) | ||
calibration_file_input = param.Parameter( | ||
doc=""" | ||
The file input widget for the calibration CSV file. | ||
""", | ||
) | ||
json_filename = param.String( | ||
default="experiment_metadata.json", | ||
doc=""" | ||
The filename which is given to the JSON file upon . | ||
""", | ||
) | ||
figure_range_slider = param.Parameter( | ||
doc=""" | ||
The slider which is used for changing the range of days which are included in the Bokeh figure. | ||
""", | ||
) | ||
|
||
def __init__(self, **params) -> None: | ||
logger.info("Initialising tomrap app") | ||
super().__init__(**params) | ||
|
||
# Define starting values for the program | ||
self.loading = True | ||
self.left_sidebar = pn.Column() | ||
self.bokeh_pane_row = pn.Row() | ||
self.centre_view = pn.Column() | ||
self.right_sidebar = pn.Column() | ||
self.date_fields = ["experiment_start_date", "experiment_end_date"] | ||
|
||
# Define default values for the program | ||
# Date fields use today's date as their default value | ||
date_now = datetime.datetime.now() | ||
|
||
# Define default element sizes for the program | ||
self.default_sizes = { | ||
"figure_range_slider": { | ||
"width": 500, | ||
}, | ||
"bokeh_pane_row": { | ||
"height": 500, | ||
}, | ||
"centre_view": { | ||
"width": 900, | ||
}, | ||
} | ||
|
||
# Values in the dictionary can be reset to their defaults when required | ||
self.default_values = { | ||
"logger_loaded": "Loaded data from %s", | ||
"df_calibrated_pressures_original": pd.DataFrame(), | ||
"df_calibrated_pressures": pd.DataFrame(), | ||
"pressure_cols": [], | ||
"df_selected_pressure_cols": pd.DataFrame(), | ||
"df_average_pressures": pd.DataFrame(), | ||
"toggle_page_size_button": { | ||
"reduce": "Reduce Page Size", | ||
"increase": "Increase Page Size", | ||
}, | ||
"metadata_status": { | ||
"active": "Metadata file settings have been applied!", | ||
"inactive": "Metadata file settings are not currently applied!", | ||
}, | ||
"metadata_date": datetime.date( | ||
year=date_now.year, | ||
month=date_now.month, | ||
day=date_now.day, | ||
), | ||
"json_filename": "experiment_metadata.json", | ||
"bokeh_figure": None, | ||
"figure_range_slider": { | ||
"start": 0, | ||
"end": 100, | ||
}, | ||
"sensor_names_table": pd.DataFrame(), | ||
"last_points_number": 50, | ||
"averages_table": pd.DataFrame(), | ||
"csv_filename": "average_calibrated_pressures.csv", | ||
} | ||
|
||
self.assign_default_data() | ||
self.create_ui_elements() | ||
self.loading = False | ||
|
||
|
||
def assign_default_data(self) -> None: | ||
""" | ||
Function to assign default data values from the default dictionary to class variables. | ||
""" | ||
self.df_calibrated_pressures_original = self.default_values["df_calibrated_pressures_original"] | ||
self.df_calibrated_pressures = self.default_values["df_calibrated_pressures"] | ||
self.pressure_cols = self.default_values["pressure_cols"] | ||
self.df_selected_pressure_cols = self.default_values["df_selected_pressure_cols"] | ||
self.df_average_pressures = self.default_values["df_average_pressures"] | ||
|
||
|
||
def create_ui_elements(self) -> None: | ||
""" | ||
Function to create the UI elements of the application | ||
and apply any require formatting/settings. | ||
""" | ||
# Create default/shared UI values | ||
self.page_size_large = True | ||
self.sidebar_title_margin = (5, 5, 5, 10) | ||
|
||
self.left_sidebar_button = pn.widgets.Button( | ||
name="=", | ||
width=25, | ||
margin=10, | ||
) | ||
self.left_sidebar_button.on_click(self.toggle_left_sidebar) | ||
self.toggle_page_size_button = pn.widgets.Button( | ||
name=self.default_values["toggle_page_size_button"]["reduce"], | ||
width=100, | ||
margin=10, | ||
) | ||
self.toggle_page_size_button.on_click(self.toggle_page_size) | ||
# Left sidebar elements | ||
self.pressures_file_input = pn.widgets.FileInput( | ||
accept=".csv", | ||
) | ||
self.calibration_file_input = pn.widgets.FileInput( | ||
accept=".csv", | ||
) | ||
self.update_data_button = pn.widgets.Button( | ||
name="Update Data", | ||
) | ||
|
||
|
||
# Centre view elements | ||
# Bokeh figure | ||
self.bokeh_pane = pn.pane.Bokeh( | ||
sizing_mode="stretch_both", | ||
) | ||
self.figure_range_slider = pn.widgets.EditableRangeSlider( | ||
name="Start/End", | ||
start=self.default_values["figure_range_slider"]["start"], | ||
end=self.default_values["figure_range_slider"]["end"], | ||
value=tuple(self.default_values["figure_range_slider"].values()), | ||
step=1, | ||
# Set the width of the slider here rather than in the view | ||
# because otherwise the slider shrinks too small | ||
width=self.default_sizes["figure_range_slider"]["width"], | ||
) | ||
|
||
|
||
def start_tomrap_app(local: bool = False): # -> pn.Column | pn.Row: | ||
""" | ||
Function to run the application. | ||
local determines if the app should start a local server | ||
or return a servable object for deployment. | ||
""" | ||
# Create an instance of the application | ||
tomrap_app_create = tomrapApp() | ||
# Create the view of the application | ||
tomrap_app = tomrap_app_create.view() | ||
title = "tomrap Analysis" | ||
|
||
if local: | ||
server = pn.serve( | ||
# Pass the application to a Panel serve function to create a server instance | ||
tomrap_app, | ||
title=title, | ||
# web_socket_max_message_size allows us to change the default max file size | ||
# when getting a file using the file picker | ||
# The default with Panel is 20MB | ||
websocket_max_message_size=int(1e9), | ||
# Specify the port the application is on | ||
port=8080, | ||
# Solve issues when deploying | ||
allow_websocket_origin=["*"], | ||
) | ||
# Run the server until the process is ended | ||
# This means the user does not have to run a Panel command to start the application | ||
# They can just run the Python script which will automatically open a browser | ||
# tab with the application in the user's default browser | ||
server.run_until_shutdown() | ||
else: | ||
tomrap_app.servable(title=title) | ||
|
||
return tomrap_app | ||
|
||
|
||
if __name__ == "__main__": | ||
tomrap_app = start_tomrap_app(local=True) | ||
else: | ||
tomrap_app = start_tomrap_app(local=False) |