diff --git a/apps/dash-turbine-maintenance/.gitignore b/apps/dash-turbine-maintenance/.gitignore deleted file mode 100644 index 3355b636c..000000000 --- a/apps/dash-turbine-maintenance/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea/* -__pycache__/ -*.ipynb_checkpoints/ - - diff --git a/apps/dash-turbine-maintenance/README.md b/apps/dash-turbine-maintenance/README.md index 3295c7e14..61faa0e3e 100644 --- a/apps/dash-turbine-maintenance/README.md +++ b/apps/dash-turbine-maintenance/README.md @@ -5,9 +5,9 @@ `dash-predictive-maintenance` is a dashboard designed to demonstrate the power of Machine Learning to predict failures (Remaining Useful Life (RUL)) in wind turbines. The data covers periods from May, 2014 to January, 2015. To predict the date when equipment will completely fail (RUL), XGBoost is used. The achieved RMSE error is `0.018534` days, which is highly accurate. ## Screenshots -![initial](screenshots/screenshot1.png) +![initial](assets/github/screenshot1.png) -![initial](screenshots/screenshot2.png) +![initial](assets/github/screenshot2.png) ## Built With * [Dash](https://dash.plot.ly/) - Main server and interactive components. diff --git a/apps/dash-turbine-maintenance/app.py b/apps/dash-turbine-maintenance/app.py index addd3e779..d03c16b06 100644 --- a/apps/dash-turbine-maintenance/app.py +++ b/apps/dash-turbine-maintenance/app.py @@ -1,14 +1,24 @@ -import pandas as pd import dash import dash_bootstrap_components as dbc -import dash_core_components as dcc -import dash_html_components as html -import dash_daq as daq - -import plotly.graph_objs as go +from dash import Dash, html, dcc, Input, Output, State, callback, callback_context from dash.dependencies import Input, Output, State -from datetime import datetime, date -from data_preprocessing import data_preprocessing + +from utils.components import ( + logo, + predict_button, + get_new_information_button, + graphs, + rul_estimation_indicator, + info_box, + active_power_display, + blade_angle_display, + active_power_from_wind_display, + wind_speed_information, + reactive_power_display, +) +from utils.figures import update_graph, display_click_data + +from constants import gauge_size import pickle app = dash.Dash( @@ -19,524 +29,6 @@ server = app.server app.title = "Predictive Maintenance Dashboard" - -def logo(app): - title = html.H5( - "PREDICTIVE MAINTENANCE DASHBOARD FOR WIND TURBINES", - style={"marginTop": 5, "marginLeft": "10px"}, - ) - - info_about_app = html.H6( - "This Dashboard is focused on estimating the Remaining Useful Life (RUL) in wind turbines. RUL is defined " - " as the time until the next fault.", - style={"marginLeft": "10px"}, - ) - - logo_image = html.Img(src=app.get_asset_url("dash-logo.png"), style={"height": 50}) - - link_btns = html.Div( - style={"float": "right"}, - children=[ - html.A( - dbc.Button("Enterprise Demo", color="primary", className="mr-1",), - href="https://plotly.com/get-demo/", - target="_blank", - ), - html.A( - dbc.Button("Source Code", color="secondary", className="mr-1"), - href="https://github.com/plotly/dash-sample-apps/tree/main/apps/dash-turbine-maintenance", - target="_blank", - ), - html.A( - logo_image, - href="https://plotly.com/dash/", - style={"margin-left": "15px"}, - ), - ], - ) - - return dbc.Row( - [dbc.Col([dbc.Row([title]), dbc.Row([info_about_app])]), dbc.Col([link_btns])] - ) - - -df, df_button, x_test, y_test = data_preprocessing() - -predict_button = dbc.Card( - className="mt-auto", - children=[ - dbc.CardBody( - [ - html.Div( - [ - dbc.Button( - "Predict", - id="predict-button", - color="#fec036", - size="lg", - style={"color": "#fec036"}, - ), - ] - ) - ], - style={ - "text-align": "center", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - "border-left": "1px solid rgb(216, 216, 216)", - "border-right": "1px solid rgb(216, 216, 216)", - "border-bottom": "1px solid rgb(216, 216, 216)", - }, - ) - ], -) - -get_new_information_button = dbc.Card( - className="mt-auto", - children=[ - dbc.CardBody( - [ - html.Div( - [ - dbc.Button( - "Get New Data", - id="get-new-info-button", - color="#fec036", - size="lg", - style={"color": "#fec036"}, - ), - ] - ) - ], - style={ - "text-align": "center", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - "border-left": "1px solid rgb(216, 216, 216)", - "border-right": "1px solid rgb(216, 216, 216)", - "border-bottom": "1px solid rgb(216, 216, 216)", - }, - ) - ], -) - - -graphs = dbc.Card( - children=[ - dbc.CardBody( - [ - html.Div( - [ - dcc.Graph( - id="Main-Graph", - figure={ - "layout": { - "margin": {"t": 30, "r": 35, "b": 40, "l": 50}, - "xaxis": { - "dtick": 5, - "gridcolor": "#636363", - "showline": False, - }, - "yaxis": {"showgrid": False, "showline": False}, - "plot_bgcolor": "black", - "paper_bgcolor": "black", - "font": {"color": "gray"}, - }, - }, - config={"displayModeBar": False}, - ), - html.Pre(id="update-on-click-data"), - ], - style={"width": "98%", "display": "inline-block"}, - ), - html.Div( - [ - dcc.Dropdown( - id="feature-dropdown", - options=[ - {"label": label, "value": label} for label in df.columns - ], - value="", - multi=False, - searchable=False, - ) - ], - style={ - "width": "33%", - "display": "inline-block", - "color": "black", - }, - ), - html.Div( - [ - dcc.DatePickerRange( - id="date-picker", - min_date_allowed=date(2014, 5, 1), # need to change this - max_date_allowed=date(2015, 4, 9), - initial_visible_month=date(2014, 5, 1), - start_date_placeholder_text="Start Period", - end_date_placeholder_text="End Period", - calendar_orientation="vertical", - ), - html.Div(id="output-container-date-picker-range"), - ], - style={ - "vertical-align": "top", - "position": "absolute", - "right": "3%", - "float": "right", - "display": "inline-block", - "color": "black", - }, - ), - ], - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ) - ] -) - -rul_estimation_indicator = dbc.Card( - children=[ - dbc.CardHeader( - "System RUL Estimation (days)", - style={ - "text-align": "center", - "color": "white", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - dbc.CardBody( - [ - daq.LEDDisplay( - id="rul-estimation-indicator-led", - size=24, - color="#fec036", - style={"color": "#black"}, - backgroundColor="#2b2b2b", - value="0.0", - ) - ], - style={ - "text-align": "center", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ] -) - -info_box = dbc.Card( - children=[ - dbc.CardBody( - [ - html.Div( - dcc.Textarea( - id="Info-Textbox", - placeholder="This field is used to display information about a feature displayed " - "on the graph and estimated RUL. In order to estimate the RUL, use " - "the button 'Get New Data' and then, 'Predict'. The estimated RUL will be " - "printed.", - rows=8, - style={ - "width": "100%", - "height": "100%", - "background-color": "black", - "color": "#fec036", - "placeholder": "#fec036", - "fontFamily": "Arial", - "fontSize": "16", - "display": "inline-block", - }, - ) - ) - ], - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ], -) - -blade_angle_display = dbc.Card( - children=[ - dbc.CardHeader( - "Blade Angle", - style={ - "text-align": "center", - "color": "white", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - dbc.CardBody( - [ - html.Div( - daq.Gauge( - id="blade-angle-information-gauge", - min=min(df["WEC: ava. blade angle A"]), - max=max( - df["WEC: ava. blade angle A"] - ), # This one should be the theoretical maximum - value=0, - showCurrentValue=True, - color="#fec036", - style={ - "align": "center", - "display": "flex", - "marginTop": "5%", - "marginBottom": "-10%", - }, - ), - className="m-auto", - style={ - "display": "flex", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - }, - ) - ], - className="d-flex", - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ], - style={"height": "95%"}, -) - -active_power_display = dbc.Card( - children=[ - dbc.CardHeader( - "Active Power [kW]", - style={ - "text-align": "center", - "color": "white", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - dbc.CardBody( - [ - html.Div( - daq.Gauge( - id="active-power-information-gauge", - min=min(df["WEC: ava. Power"]), - max=max( - df["WEC: ava. Power"] - ), # This one should be the theoretical maximum - value=100, - showCurrentValue=True, - color="#fec036", - style={ - "align": "center", - "display": "flex", - "marginTop": "5%", - "marginBottom": "-10%", - }, - ), - className="m-auto", - style={ - "display": "flex", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - }, - ) - ], - className="d-flex", - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ], - style={"height": "95%"}, -) - -active_power_from_wind_display = dbc.Card( - children=[ - dbc.CardHeader( - "Active Power Available from Wind [kW]", - style={ - "display": "inline-block", - "text-align": "center", - "color": "white", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - dbc.CardBody( - [ - html.Div( - daq.Gauge( - id="active-power-from-wind-information-gauge", - min=min(df["WEC: ava. available P from wind"]), - max=max(df["WEC: ava. available P from wind"]), - value=10, - showCurrentValue=True, - color="#fec036", - style={ - "align": "center", - "display": "flex", - "marginTop": "5%", - "marginBottom": "-10%", - }, - ), - className="m-auto", - style={ - "display": "flex", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - }, - ) - ], - className="d-flex", - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ], - style={"height": "95%"}, -) - -wind_speed_information = dbc.Card( - className="mt-auto", - children=[ - dbc.CardHeader( - "Wind Speed [m/s]", - style={ - "text-align": "center", - "color": "white", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - dbc.CardBody( - [ - html.Div( - daq.Gauge( - id="wind-power-information-gauge", - min=min(df["WEC: ava. windspeed"]), - max=int(max(df["WEC: ava. windspeed"])), - value=0, - showCurrentValue=True, - color="#fec036", - style={ - "align": "center", - "display": "flex", - "marginTop": "5%", - "marginBottom": "-10%", - }, - ), - className="m-auto", - style={ - "display": "flex", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - }, - ) - ], - className="d-flex", - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ], - style={"height": "95%"}, -) - -reactive_power_display = dbc.Card( - className="mt-auto", - children=[ - dbc.CardHeader( - "Reactive Power [kVAR]", - style={ - "text-align": "center", - "color": "white", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - dbc.CardBody( - [ - html.Div( - daq.Gauge( - id="reactive-power-information-gauge", - min=min(df["WEC: ava. reactive Power"]), - max=max(df["WEC: ava. reactive Power"]), - value=0, - showCurrentValue=True, - color="#fec036", - style={ - "align": "center", - "display": "flex", - "marginTop": "5%", - "marginBottom": "-10%", - }, - ), - className="m-auto", - style={ - "display": "flex", - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - }, - ) - ], - className="d-flex", - style={ - "backgroundColor": "black", - "border-radius": "1px", - "border-width": "5px", - "border-top": "1px solid rgb(216, 216, 216)", - }, - ), - ], - style={"height": "95%"}, -) - -gauge_size = "auto" app.layout = dbc.Container( fluid=True, children=[ @@ -612,341 +104,30 @@ def logo(app): ) -def fig_update_layout(fig): - fig.update_layout( - xaxis=dict( - showline=False, - showgrid=False, - showticklabels=True, - zeroline=False, - gridcolor="#636363", - linecolor="rgb(204, 204, 204)", - linewidth=2, - tickfont=dict(family="Arial", size=12, color="white",), - title=dict(font=dict(family="Arial", size=24, color="#fec036"),), - ), - yaxis=dict( - showline=False, - showgrid=False, - showticklabels=True, - zeroline=False, - gridcolor="#636363", - linecolor="rgb(204, 204, 204)", - linewidth=2, - tickfont=dict(family="Arial", size=12, color="white",), - title=dict(font=dict(family="Arial", size=24, color="#fec036"),), - ), - autosize=True, - margin=dict(autoexpand=True, l=50, b=40, r=35, t=30), - showlegend=False, - paper_bgcolor="black", - plot_bgcolor="black", - title=dict( - font=dict(family="Arial", size=32, color="darkgray"), - xanchor="center", - yanchor="top", - y=1, - x=0.5, - ), - ) - return fig - - -@app.callback( - [ - Output("Main-Graph", "figure"), - Output("rul-estimation-indicator-led", "value"), - Output("Info-Textbox", "value"), - ], - [ - Input("feature-dropdown", "value"), - Input("date-picker", "start_date"), - Input("date-picker", "end_date"), - Input("get-new-info-button", "n_clicks"), - Input("predict-button", "n_clicks"), - ], +@callback( + Output("Main-Graph", "figure"), + Output("rul-estimation-indicator-led", "value"), + Output("Info-Textbox", "value"), + Input("feature-dropdown", "value"), + Input("date-picker", "start_date"), + Input("date-picker", "end_date"), + Input("get-new-info-button", "n_clicks"), + Input("predict-button", "n_clicks"), ) -def update_graph(selected_column, start_date, end_date, n_get_new_info, n_pred): - if n_pred is None: # here is my work before prediction button is activated. - value_rul = 0.0 - information_update = ( - "This field is used to display information about a feature displayed " - "on the graph and estimated RUL. In order to estimate the RUL, use " - "the button 'Get New Data' and then, 'Predict'. The estimated RUL will be " - "printed." - ) - if n_get_new_info is None: - if selected_column in list(df): - if start_date and end_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - end_date_object = datetime.strptime(end_date, "%Y-%m-%d") - mask = (df.index > start_date_object) & ( - df.index <= end_date_object - ) - df_within_dates = df.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - elif start_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - mask = df.index > start_date_object - df_within_dates = df.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - fig = go.Figure( - data=[go.Scatter(x=df.index, y=df[selected_column])] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - fig = go.Figure() - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - if selected_column in list(df_button): - if start_date and end_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - end_date_object = datetime.strptime(end_date, "%Y-%m-%d") - mask = (df_button.index > start_date_object) & ( - df_button.index <= end_date_object - ) - df_within_dates = df_button.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - _information_update = ( - "New information is received for the last week and covers periods from " - + str(df_button.index[0]) - + " to " - + str(df_button.index[-1]) - + ". To predict" - " RUL, use 'Predict' button. To view data for the aforementioned period, choose" - " appropriate dates on the calendar." - ) - return fig, value_rul, _information_update - elif start_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - mask = df_button.index > start_date_object - df_within_dates = df_button.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - _information_update = ( - "New information is received for the last week and covers periods from " - + str(df_button.index[0]) - + " to " - + str(df_button.index[-1]) - + ". To predict" - " RUL, use 'Predict' button. To view data for the aforementioned period, choose" - " appropriate dates on the calendar." - ) - return fig, value_rul, _information_update - else: - fig = go.Figure( - data=[ - go.Scatter(x=df_button.index, y=df_button[selected_column]) - ] - ) - fig = fig_update_layout(fig) - _information_update = ( - "New information is received for the last week and covers periods from " - + str(df_button.index[0]) - + " to " - + str(df_button.index[-1]) - + ". To predict" - " RUL, use 'Predict' button. To view data for the aforementioned period, choose" - " appropriate dates on the calendar." - ) - return fig, value_rul, _information_update - else: - fig = go.Figure() - fig = fig_update_layout(fig) - _information_update = ( - "New information is received for the last week and covers periods from " - + str(df_button.index[0]) - + " to " - + str(df_button.index[-1]) - + ". To predict" - " RUL, use 'Predict' button. To view data for the aforementioned period, choose" - " appropriate dates on the calendar." - ) - return fig, value_rul, _information_update - else: - if n_get_new_info is None: - value_rul = 0.0 - information_update = "To predict RUL, please use 'Get New Data' button." - if selected_column in list(df): - if start_date and end_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - end_date_object = datetime.strptime(end_date, "%Y-%m-%d") - mask = (df.index > start_date_object) & ( - df.index <= end_date_object - ) - df_within_dates = df.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - elif start_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - mask = df.index > start_date_object - df_within_dates = df.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - fig = go.Figure( - data=[go.Scatter(x=df.index, y=df[selected_column])] - ) - return fig, value_rul, information_update - else: - fig = go.Figure() - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - model = pickle.load(open("assets/xgb_reg.pkl", "rb")) - y_pred = model.predict(x_test) - df_out = pd.DataFrame() - df_out["pred"] = y_pred - value_rul = round(max(df_out["pred"])) - information_update = "RUL is estimated based on the readings from the last week: " "from " + str( - x_test.index[0] - ) + " to " + str( - x_test.index[-1] - ) - if selected_column in list(df_button): - if start_date and end_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - end_date_object = datetime.strptime(end_date, "%Y-%m-%d") - mask = (df_button.index > start_date_object) & ( - df_button.index <= end_date_object - ) - df_within_dates = df_button.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - elif start_date: - start_date_object = datetime.strptime(start_date, "%Y-%m-%d") - mask = df_button.index > start_date_object - df_within_dates = df_button.loc[mask] - fig = go.Figure( - data=[ - go.Scatter( - x=df_within_dates.index, - y=df_within_dates[selected_column], - ) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - fig = go.Figure( - data=[ - go.Scatter(x=df_button.index, y=df_button[selected_column]) - ] - ) - fig = fig_update_layout(fig) - return fig, value_rul, information_update - else: - fig = go.Figure() - fig = fig_update_layout(fig) - return fig, value_rul, information_update +def return_updated_graph(selected_column, start_date, end_date, n_get_new_info, n_pred): + return update_graph(selected_column, start_date, end_date, n_get_new_info, n_pred) -@app.callback( - [ - Output("active-power-information-gauge", "value"), - Output("active-power-from-wind-information-gauge", "value"), - Output("wind-power-information-gauge", "value"), - Output("reactive-power-information-gauge", "value"), - Output("blade-angle-information-gauge", "value"), - ], +@callback( + Output("active-power-information-gauge", "value"), + Output("active-power-from-wind-information-gauge", "value"), + Output("wind-power-information-gauge", "value"), + Output("reactive-power-information-gauge", "value"), + Output("blade-angle-information-gauge", "value"), Input("Main-Graph", "clickData"), ) -def display_click_data(clickData): - if clickData: - data_time = clickData["points"][0]["x"] - value_active_power = df["WEC: ava. Power"].loc[df.index == data_time].values[0] - value_active_power_wind = ( - df["WEC: ava. available P from wind"].loc[df.index == data_time].values[0] - ) - value_reactive_power = ( - df["WEC: ava. reactive Power"].loc[df.index == data_time].values[0] - ) - value_wind_speed = ( - df["WEC: ava. windspeed"].loc[df.index == data_time].values[0] - ) - value_blade_angle = ( - df["WEC: ava. blade angle A"].loc[df.index == data_time].values[0] - ) - return ( - value_active_power, - value_active_power_wind, - value_wind_speed, - value_reactive_power, - value_blade_angle, - ) - else: - value_active_power = 0 - value_active_power_wind = 0 - value_reactive_power = 0 - value_wind_speed = 0 - value_blade_angle = 0 - return ( - value_active_power, - value_active_power_wind, - value_wind_speed, - value_reactive_power, - value_blade_angle, - ) +def _return_displayed_click_data(clickData): + return display_click_data(clickData) if __name__ == "__main__": diff --git a/apps/dash-turbine-maintenance/screenshots/screenshot1.png b/apps/dash-turbine-maintenance/assets/github/screenshot1.png similarity index 100% rename from apps/dash-turbine-maintenance/screenshots/screenshot1.png rename to apps/dash-turbine-maintenance/assets/github/screenshot1.png diff --git a/apps/dash-turbine-maintenance/screenshots/screenshot2.png b/apps/dash-turbine-maintenance/assets/github/screenshot2.png similarity index 100% rename from apps/dash-turbine-maintenance/screenshots/screenshot2.png rename to apps/dash-turbine-maintenance/assets/github/screenshot2.png diff --git a/apps/dash-turbine-maintenance/assets/dash-logo.png b/apps/dash-turbine-maintenance/assets/images/dash-logo.png similarity index 100% rename from apps/dash-turbine-maintenance/assets/dash-logo.png rename to apps/dash-turbine-maintenance/assets/images/dash-logo.png diff --git a/apps/dash-turbine-maintenance/constants.py b/apps/dash-turbine-maintenance/constants.py new file mode 100644 index 000000000..3d3a26245 --- /dev/null +++ b/apps/dash-turbine-maintenance/constants.py @@ -0,0 +1 @@ +gauge_size = "auto" diff --git a/apps/dash-turbine-maintenance/gitignore b/apps/dash-turbine-maintenance/gitignore new file mode 100644 index 000000000..d8e187da3 --- /dev/null +++ b/apps/dash-turbine-maintenance/gitignore @@ -0,0 +1,191 @@ +# .gitignore specifies the files that shouldn't be included +# in version control and therefore shouldn't be included when +# deploying an application to Dash Enterprise +# This is a very exhaustive list! +# This list was based off of https://github.com/github/gitignore + +# Ignore data that is generated during the runtime of an application +# This folder is used by the "Large Data" sample applications +runtime_data/ +data/ + +# Omit SQLite databases that may be produced by dash-snapshots in development +*.db + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + + +# Jupyter Notebook + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + + +# macOS General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# History files +.Rhistory +.Rapp.history + +# Session Data files +.RData + +# User-specific files +.Ruserdata + +# Example code in package build process +*-Ex.R + +# Output files from R CMD check +/*.Rcheck/ + +# RStudio files +.Rproj.user/ + +# produced vignettes +vignettes/*.html +vignettes/*.pdf + +# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 +.httr-oauth + +# knitr and R markdown default cache directories +*_cache/ +/cache/ + +# Temporary files created by R markdown +*.utf8.md +*.knit.md + +# R Environment Variables +.Renviron + +# Linux +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# SublineText +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings \ No newline at end of file diff --git a/apps/dash-turbine-maintenance/requirements.txt b/apps/dash-turbine-maintenance/requirements.txt index 6d63335c1..c57daddb2 100644 --- a/apps/dash-turbine-maintenance/requirements.txt +++ b/apps/dash-turbine-maintenance/requirements.txt @@ -1,15 +1,11 @@ -dash==1.19.0 -dash-bootstrap-components==0.11.3 -dash-core-components==1.15.0 +dash==2.4.1 +pandas==1.4.2 +gunicorn==20.1.0 +dash-bootstrap-components==1.1.0 dash-daq==0.5.0 -dash-html-components==1.1.2 -dash-renderer==1.9.0 -dash-table==4.11.2 +dash-renderer==1.9.1 matplotlib==3.3.4 -numpy==1.19.5 -pandas==1.1.5 -plotly==4.14.3 scikit-learn==0.24.1 scipy==1.5.4 xgboost==1.3.3 -gunicorn \ No newline at end of file + diff --git a/apps/dash-turbine-maintenance/runtime.txt b/apps/dash-turbine-maintenance/runtime.txt new file mode 100644 index 000000000..cfa660c42 --- /dev/null +++ b/apps/dash-turbine-maintenance/runtime.txt @@ -0,0 +1 @@ +python-3.8.0 \ No newline at end of file diff --git a/apps/dash-turbine-maintenance/utils/components.py b/apps/dash-turbine-maintenance/utils/components.py new file mode 100644 index 000000000..dd5812596 --- /dev/null +++ b/apps/dash-turbine-maintenance/utils/components.py @@ -0,0 +1,553 @@ +from dash import Dash, html, dcc, Input, Output, State, callback, callback_context +import dash_bootstrap_components as dbc +import dash_daq as daq +from utils.helper_functions import df +from datetime import datetime, date +from constants import gauge_size + + +def header( + app, header_color, header, subheader=None, header_background_color="transparent" +): + left_headers = html.Div( + [ + html.Div(header, className="header-title"), + html.Div(subheader, className="subheader-title"), + ], + style={"color": header_color}, + ) + + logo = html.Img(src=app.get_asset_url("images/plotly-logo-light-theme.png")) + logo_link = html.A(logo, href="https://plotly.com/get-demo/", target="_blank") + demo_link = html.A( + "LEARN MORE", + href="https://plotly.com/dash/", + target="_blank", + className="demo-button", + ) + right_logos = html.Div([demo_link, logo_link], className="header-logos") + + return html.Div( + [left_headers, right_logos], + className="header", + style={"background-color": header_background_color}, + ) + + +def logo(app): + title = html.H5( + "PREDICTIVE MAINTENANCE DASHBOARD FOR WIND TURBINES", + style={"marginTop": 5, "marginLeft": "10px"}, + ) + + info_about_app = html.H6( + "This Dashboard is focused on estimating the Remaining Useful Life (RUL) in wind turbines. RUL is defined " + " as the time until the next fault.", + style={"marginLeft": "10px"}, + ) + + logo_image = html.Img(src=app.get_asset_url("dash-logo.png"), style={"height": 50}) + + link_btns = html.Div( + style={"float": "right"}, + children=[ + html.A( + dbc.Button( + "Enterprise Demo", + color="primary", + className="mr-1", + ), + href="https://plotly.com/get-demo/", + target="_blank", + ), + html.A( + dbc.Button("Source Code", color="secondary", className="mr-1"), + href="https://github.com/plotly/dash-sample-apps/tree/main/apps/dash-turbine-maintenance", + target="_blank", + ), + html.A( + logo_image, + href="https://plotly.com/dash/", + style={"margin-left": "15px"}, + ), + ], + ) + + return dbc.Row( + [dbc.Col([dbc.Row([title]), dbc.Row([info_about_app])]), dbc.Col([link_btns])] + ) + + +predict_button = dbc.Card( + className="mt-auto", + children=[ + dbc.CardBody( + [ + html.Div( + [ + dbc.Button( + "Predict", + id="predict-button", + color="#fec036", + size="lg", + style={"color": "#fec036"}, + ), + ] + ) + ], + style={ + "text-align": "center", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + "border-left": "1px solid rgb(216, 216, 216)", + "border-right": "1px solid rgb(216, 216, 216)", + "border-bottom": "1px solid rgb(216, 216, 216)", + }, + ) + ], +) + +get_new_information_button = dbc.Card( + className="mt-auto", + children=[ + dbc.CardBody( + [ + html.Div( + [ + dbc.Button( + "Get New Data", + id="get-new-info-button", + color="#fec036", + size="lg", + style={"color": "#fec036"}, + ), + ] + ) + ], + style={ + "text-align": "center", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + "border-left": "1px solid rgb(216, 216, 216)", + "border-right": "1px solid rgb(216, 216, 216)", + "border-bottom": "1px solid rgb(216, 216, 216)", + }, + ) + ], +) + + +graphs = dbc.Card( + children=[ + dbc.CardBody( + [ + html.Div( + [ + dcc.Graph( + id="Main-Graph", + figure={ + "layout": { + "margin": {"t": 30, "r": 35, "b": 40, "l": 50}, + "xaxis": { + "dtick": 5, + "gridcolor": "#636363", + "showline": False, + }, + "yaxis": {"showgrid": False, "showline": False}, + "plot_bgcolor": "black", + "paper_bgcolor": "black", + "font": {"color": "gray"}, + }, + }, + config={"displayModeBar": False}, + ), + html.Pre(id="update-on-click-data"), + ], + style={"width": "98%", "display": "inline-block"}, + ), + html.Div( + [ + dcc.Dropdown( + id="feature-dropdown", + options=[ + {"label": label, "value": label} for label in df.columns + ], + value="", + multi=False, + searchable=False, + ) + ], + style={ + "width": "33%", + "display": "inline-block", + "color": "black", + }, + ), + html.Div( + [ + dcc.DatePickerRange( + id="date-picker", + min_date_allowed=date(2014, 5, 1), # need to change this + max_date_allowed=date(2015, 4, 9), + initial_visible_month=date(2014, 5, 1), + start_date_placeholder_text="Start Period", + end_date_placeholder_text="End Period", + calendar_orientation="vertical", + ), + html.Div(id="output-container-date-picker-range"), + ], + style={ + "vertical-align": "top", + "position": "absolute", + "right": "3%", + "float": "right", + "display": "inline-block", + "color": "black", + }, + ), + ], + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ) + ] +) + +rul_estimation_indicator = dbc.Card( + children=[ + dbc.CardHeader( + "System RUL Estimation (days)", + style={ + "text-align": "center", + "color": "white", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + dbc.CardBody( + [ + daq.LEDDisplay( + id="rul-estimation-indicator-led", + size=24, + color="#fec036", + style={"color": "#black"}, + backgroundColor="#2b2b2b", + value="0.0", + ) + ], + style={ + "text-align": "center", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ] +) + +info_box = dbc.Card( + children=[ + dbc.CardBody( + [ + html.Div( + dcc.Textarea( + id="Info-Textbox", + placeholder="This field is used to display information about a feature displayed " + "on the graph and estimated RUL. In order to estimate the RUL, use " + "the button 'Get New Data' and then, 'Predict'. The estimated RUL will be " + "printed.", + rows=8, + style={ + "width": "100%", + "height": "100%", + "background-color": "black", + "color": "#fec036", + "placeholder": "#fec036", + "fontFamily": "Arial", + "fontSize": "16", + "display": "inline-block", + }, + ) + ) + ], + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ], +) + +blade_angle_display = dbc.Card( + children=[ + dbc.CardHeader( + "Blade Angle", + style={ + "text-align": "center", + "color": "white", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + dbc.CardBody( + [ + html.Div( + daq.Gauge( + id="blade-angle-information-gauge", + min=min(df["WEC: ava. blade angle A"]), + max=max( + df["WEC: ava. blade angle A"] + ), # This one should be the theoretical maximum + value=0, + showCurrentValue=True, + color="#fec036", + style={ + "align": "center", + "display": "flex", + "marginTop": "5%", + "marginBottom": "-10%", + }, + ), + className="m-auto", + style={ + "display": "flex", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + }, + ) + ], + className="d-flex", + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ], + style={"height": "95%"}, +) + +active_power_display = dbc.Card( + children=[ + dbc.CardHeader( + "Active Power [kW]", + style={ + "text-align": "center", + "color": "white", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + dbc.CardBody( + [ + html.Div( + daq.Gauge( + id="active-power-information-gauge", + min=min(df["WEC: ava. Power"]), + max=max( + df["WEC: ava. Power"] + ), # This one should be the theoretical maximum + value=100, + showCurrentValue=True, + color="#fec036", + style={ + "align": "center", + "display": "flex", + "marginTop": "5%", + "marginBottom": "-10%", + }, + ), + className="m-auto", + style={ + "display": "flex", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + }, + ) + ], + className="d-flex", + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ], + style={"height": "95%"}, +) + +active_power_from_wind_display = dbc.Card( + children=[ + dbc.CardHeader( + "Active Power Available from Wind [kW]", + style={ + "display": "inline-block", + "text-align": "center", + "color": "white", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + dbc.CardBody( + [ + html.Div( + daq.Gauge( + id="active-power-from-wind-information-gauge", + min=min(df["WEC: ava. available P from wind"]), + max=max(df["WEC: ava. available P from wind"]), + value=10, + showCurrentValue=True, + color="#fec036", + style={ + "align": "center", + "display": "flex", + "marginTop": "5%", + "marginBottom": "-10%", + }, + ), + className="m-auto", + style={ + "display": "flex", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + }, + ) + ], + className="d-flex", + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ], + style={"height": "95%"}, +) + +wind_speed_information = dbc.Card( + className="mt-auto", + children=[ + dbc.CardHeader( + "Wind Speed [m/s]", + style={ + "text-align": "center", + "color": "white", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + dbc.CardBody( + [ + html.Div( + daq.Gauge( + id="wind-power-information-gauge", + min=min(df["WEC: ava. windspeed"]), + max=int(max(df["WEC: ava. windspeed"])), + value=0, + showCurrentValue=True, + color="#fec036", + style={ + "align": "center", + "display": "flex", + "marginTop": "5%", + "marginBottom": "-10%", + }, + ), + className="m-auto", + style={ + "display": "flex", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + }, + ) + ], + className="d-flex", + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ], + style={"height": "95%"}, +) + +reactive_power_display = dbc.Card( + className="mt-auto", + children=[ + dbc.CardHeader( + "Reactive Power [kVAR]", + style={ + "text-align": "center", + "color": "white", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + dbc.CardBody( + [ + html.Div( + daq.Gauge( + id="reactive-power-information-gauge", + min=min(df["WEC: ava. reactive Power"]), + max=max(df["WEC: ava. reactive Power"]), + value=0, + showCurrentValue=True, + color="#fec036", + style={ + "align": "center", + "display": "flex", + "marginTop": "5%", + "marginBottom": "-10%", + }, + ), + className="m-auto", + style={ + "display": "flex", + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + }, + ) + ], + className="d-flex", + style={ + "backgroundColor": "black", + "border-radius": "1px", + "border-width": "5px", + "border-top": "1px solid rgb(216, 216, 216)", + }, + ), + ], + style={"height": "95%"}, +) diff --git a/apps/dash-turbine-maintenance/utils/figures.py b/apps/dash-turbine-maintenance/utils/figures.py new file mode 100644 index 000000000..0f05f3530 --- /dev/null +++ b/apps/dash-turbine-maintenance/utils/figures.py @@ -0,0 +1,277 @@ +from datetime import datetime, date +import plotly.graph_objs as go +from utils.helper_functions import df, df_button, x_test, fig_update_layout +import pickle +import pandas as pd + + +def update_graph(selected_column, start_date, end_date, n_get_new_info, n_pred): + if n_pred is None: # here is my work before prediction button is activated. + value_rul = 0.0 + information_update = ( + "This field is used to display information about a feature displayed " + "on the graph and estimated RUL. In order to estimate the RUL, use " + "the button 'Get New Data' and then, 'Predict'. The estimated RUL will be " + "printed." + ) + if n_get_new_info is None: + if selected_column in list(df): + if start_date and end_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + end_date_object = datetime.strptime(end_date, "%Y-%m-%d") + mask = (df.index > start_date_object) & ( + df.index <= end_date_object + ) + df_within_dates = df.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + elif start_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + mask = df.index > start_date_object + df_within_dates = df.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + fig = go.Figure( + data=[go.Scatter(x=df.index, y=df[selected_column])] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + fig = go.Figure() + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + if selected_column in list(df_button): + if start_date and end_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + end_date_object = datetime.strptime(end_date, "%Y-%m-%d") + mask = (df_button.index > start_date_object) & ( + df_button.index <= end_date_object + ) + df_within_dates = df_button.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + _information_update = ( + "New information is received for the last week and covers periods from " + + str(df_button.index[0]) + + " to " + + str(df_button.index[-1]) + + ". To predict" + " RUL, use 'Predict' button. To view data for the aforementioned period, choose" + " appropriate dates on the calendar." + ) + return fig, value_rul, _information_update + elif start_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + mask = df_button.index > start_date_object + df_within_dates = df_button.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + _information_update = ( + "New information is received for the last week and covers periods from " + + str(df_button.index[0]) + + " to " + + str(df_button.index[-1]) + + ". To predict" + " RUL, use 'Predict' button. To view data for the aforementioned period, choose" + " appropriate dates on the calendar." + ) + return fig, value_rul, _information_update + else: + fig = go.Figure( + data=[ + go.Scatter(x=df_button.index, y=df_button[selected_column]) + ] + ) + fig = fig_update_layout(fig) + _information_update = ( + "New information is received for the last week and covers periods from " + + str(df_button.index[0]) + + " to " + + str(df_button.index[-1]) + + ". To predict" + " RUL, use 'Predict' button. To view data for the aforementioned period, choose" + " appropriate dates on the calendar." + ) + return fig, value_rul, _information_update + else: + fig = go.Figure() + fig = fig_update_layout(fig) + _information_update = ( + "New information is received for the last week and covers periods from " + + str(df_button.index[0]) + + " to " + + str(df_button.index[-1]) + + ". To predict" + " RUL, use 'Predict' button. To view data for the aforementioned period, choose" + " appropriate dates on the calendar." + ) + return fig, value_rul, _information_update + else: + if n_get_new_info is None: + value_rul = 0.0 + information_update = "To predict RUL, please use 'Get New Data' button." + if selected_column in list(df): + if start_date and end_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + end_date_object = datetime.strptime(end_date, "%Y-%m-%d") + mask = (df.index > start_date_object) & ( + df.index <= end_date_object + ) + df_within_dates = df.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + elif start_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + mask = df.index > start_date_object + df_within_dates = df.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + fig = go.Figure( + data=[go.Scatter(x=df.index, y=df[selected_column])] + ) + return fig, value_rul, information_update + else: + fig = go.Figure() + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + model = pickle.load(open("assets/xgb_reg.pkl", "rb")) + y_pred = model.predict(x_test) + df_out = pd.DataFrame() + df_out["pred"] = y_pred + value_rul = round(max(df_out["pred"])) + information_update = ( + "RUL is estimated based on the readings from the last week: " + "from " + str(x_test.index[0]) + " to " + str(x_test.index[-1]) + ) + if selected_column in list(df_button): + if start_date and end_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + end_date_object = datetime.strptime(end_date, "%Y-%m-%d") + mask = (df_button.index > start_date_object) & ( + df_button.index <= end_date_object + ) + df_within_dates = df_button.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + elif start_date: + start_date_object = datetime.strptime(start_date, "%Y-%m-%d") + mask = df_button.index > start_date_object + df_within_dates = df_button.loc[mask] + fig = go.Figure( + data=[ + go.Scatter( + x=df_within_dates.index, + y=df_within_dates[selected_column], + ) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + fig = go.Figure( + data=[ + go.Scatter(x=df_button.index, y=df_button[selected_column]) + ] + ) + fig = fig_update_layout(fig) + return fig, value_rul, information_update + else: + fig = go.Figure() + fig = fig_update_layout(fig) + return fig, value_rul, information_update + + +def display_click_data(clickData): + if clickData: + data_time = clickData["points"][0]["x"] + value_active_power = df["WEC: ava. Power"].loc[df.index == data_time].values[0] + value_active_power_wind = ( + df["WEC: ava. available P from wind"].loc[df.index == data_time].values[0] + ) + value_reactive_power = ( + df["WEC: ava. reactive Power"].loc[df.index == data_time].values[0] + ) + value_wind_speed = ( + df["WEC: ava. windspeed"].loc[df.index == data_time].values[0] + ) + value_blade_angle = ( + df["WEC: ava. blade angle A"].loc[df.index == data_time].values[0] + ) + return ( + value_active_power, + value_active_power_wind, + value_wind_speed, + value_reactive_power, + value_blade_angle, + ) + else: + value_active_power = 0 + value_active_power_wind = 0 + value_reactive_power = 0 + value_wind_speed = 0 + value_blade_angle = 0 + return ( + value_active_power, + value_active_power_wind, + value_wind_speed, + value_reactive_power, + value_blade_angle, + ) diff --git a/apps/dash-turbine-maintenance/data_preprocessing.py b/apps/dash-turbine-maintenance/utils/helper_functions.py similarity index 74% rename from apps/dash-turbine-maintenance/data_preprocessing.py rename to apps/dash-turbine-maintenance/utils/helper_functions.py index 53f4d7d37..1fae3cc21 100644 --- a/apps/dash-turbine-maintenance/data_preprocessing.py +++ b/apps/dash-turbine-maintenance/utils/helper_functions.py @@ -121,3 +121,58 @@ def data_preprocessing(): df_test = x_test.iloc[:, : df.shape[1]] return df, df_test, x_test, y_test + + +df, df_button, x_test, y_test = data_preprocessing() + + +def fig_update_layout(fig): + fig.update_layout( + xaxis=dict( + showline=False, + showgrid=False, + showticklabels=True, + zeroline=False, + gridcolor="#636363", + linecolor="rgb(204, 204, 204)", + linewidth=2, + tickfont=dict( + family="Arial", + size=12, + color="white", + ), + title=dict( + font=dict(family="Arial", size=24, color="#fec036"), + ), + ), + yaxis=dict( + showline=False, + showgrid=False, + showticklabels=True, + zeroline=False, + gridcolor="#636363", + linecolor="rgb(204, 204, 204)", + linewidth=2, + tickfont=dict( + family="Arial", + size=12, + color="white", + ), + title=dict( + font=dict(family="Arial", size=24, color="#fec036"), + ), + ), + autosize=True, + margin=dict(autoexpand=True, l=50, b=40, r=35, t=30), + showlegend=False, + paper_bgcolor="black", + plot_bgcolor="black", + title=dict( + font=dict(family="Arial", size=32, color="darkgray"), + xanchor="center", + yanchor="top", + y=1, + x=0.5, + ), + ) + return fig