Skip to content

Commit

Permalink
Hazmap App Branch
Browse files Browse the repository at this point in the history
  • Loading branch information
dvalters committed Feb 29, 2024
1 parent 0647107 commit 16dd646
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 0 deletions.
Empty file added app/__init__.py
Empty file.
77 changes: 77 additions & 0 deletions app/hazards.css
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;
}
231 changes: 231 additions & 0 deletions app/main.py
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)

0 comments on commit 16dd646

Please sign in to comment.