#!/usr/bin/env python # -*- coding: utf-8 -*- import argparse import datetime import logging import os import traceback from typing import Dict, List # import dash_html_components as html # import flask import numpy as np import plotly.express as px from dash import Dash, Input, Output, State # , dcc # ,html from dash.exceptions import PreventUpdate from flask import Flask, render_template from libs.dashapp import layout, open_image, perform_analysis from libs.utils import setup_logging from libs.utils import verbose as vprint setup_logging() log = logging.getLogger(__name__) CONFIG = {} V = 1 V_IGNORE = [] # Debug, Warning, Error MODEL_PATH = "" FARM_NAME = "" # =============================================================================== # The Main App # =============================================================================== app = Flask(__name__) @app.route("/") def home(): return render_template( "index.html" ) # "Welcome to Nowcasting App.

The dashapp is still under development..." @app.route("/register") def register(): return "Welcome to Nowcasting signup page.

The dashapp is still under development..." @app.route("/addjob") def addjob(): return "Welcome to Nowcasting signup page.

The dashapp is still under development..." @app.route("/dashboard") def dashboard(): return "Welcome to Nowcasting dashboard page.

This page will contain dashboard for the prediction pipelines, and allow you to manage and add new predictions.
The dashapp is still under development..." # dashapp # =============================================================================== # Soil Moisture Comparison Tool App Layout # =============================================================================== # external JavaScript files external_scripts = [ "https://www.google-analytics.com/analytics.js", {"src": "https://cdn.polyfill.io/v2/polyfill.min.js"}, { "src": "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js", "integrity": "sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=", "crossorigin": "anonymous", }, ] # external CSS stylesheets external_stylesheets = [ "https://codepen.io/chriddyp/pen/bWLwgP.css", { "href": "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css", "rel": "stylesheet", "integrity": "sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO", "crossorigin": "anonymous", }, ] dashapp = Dash( __name__, server=app, routes_pathname_prefix="/dashapp/", external_scripts=external_scripts, external_stylesheets=external_stylesheets, title="Soil Moisture Comparison Tool", update_title="Loading the tool...", ) # farm_name = "Arawa" # layer = "SM2" time_delta = datetime.timedelta(days=20) FAIL_IMAGE = dashapp.get_asset_url("icons/fail.png") SUCCESS_IMAGE = dashapp.get_asset_url("icons/success.png") WAIT_IMAGE = dashapp.get_asset_url("icons/wait.png") current_working_directory = os.getcwd() dashapp.index_template = os.path.join( current_working_directory, "templates", "dashapp.html" ) dashapp.layout = layout(WAIT_IMAGE) def find_model_path(real_path): for root, dirs, _ in os.walk(real_path): # print(root,dirs,"\n\n") if "soilwatermodel" in dirs: real_path = os.path.join(root, "soilwatermodel") print("real_path is", real_path) return real_path return real_path # ==================================================================================================== # Callbacks # ==================================================================================================== @dashapp.callback( [ Output("farm-name-session", "data"), Output("farm-image", "src"), ], [Input("farm-name", "value"), State("farm-name-session", "data")], ) def update_session(farm_name, session): global MODEL_PATH global FARM_NAME session = farm_name if farm_name is None or farm_name == "": session = "" image = WAIT_IMAGE else: print(f"Getting some data about farm: {farm_name}") # if the path does not exist, do not update the session real_path = INPUT.format(farm_name) print(f"Checking: {real_path}") try: path = find_model_path(real_path) real_path = path except Exception: print("Exception raised while searching for the root") MODEL_PATH = path if os.path.exists(real_path): session = farm_name FARM_NAME = farm_name image = SUCCESS_IMAGE else: session = "" image = FAIL_IMAGE print(f"\n\nSession updated to {session}") print(f"Image updated to {image}\n\n") return session, image @dashapp.callback( Output("farm-name", "value"), Input("farm-name-session", "modified_timestamp"), State("farm-name-session", "data"), ) def display_name_from_session(timestamp, name): print(f"Updating the farm name from the session: {name}") if timestamp is not None: return name else: return "" @dashapp.callback( Output("visualisation-select", "options"), # Input("farm-name", "value"), Input("layer-dropdown", "value"), Input("window-select", "start_date"), Input("window-select", "end_date"), Input("historic-dropdown", "value"), Input("w-aggregation-dropdown", "value"), Input("h-aggregation-dropdown", "value"), Input("generate-button", "n_clicks"), State("farm-name-session", "data"), ) def get_analysis( layer, window_start, window_end, historic_years, w_agg, h_agg, n_clicks, farm_name ) -> List[Dict[str, str]]: """Get the analysis files and return them as a list of dicts. Parameters ---------- layer : str layer to use for the analysis window_start : str start date of the window window_end : str end date of the window historic_years : int number of years to use for the historic data w_agg : str aggregation method for the window data h_agg : str aggregation method for the historic data n_clicks : int number of times the generate button has been clicked Returns ------- files : list list of dicts of analysis files """ global MODEL_PATH global FARM_NAME print("\nAnalysis callback triggered") if n_clicks == 0 or n_clicks is None: raise PreventUpdate # window_start = datetime.datetime.strptime(window_start, '%Y-%m-%d') # window_end = datetime.datetime.strptime(window_end, '%Y-%m-%d') print(f"\nPath: {MODEL_PATH}\n") files = perform_analysis( input=MODEL_PATH, window_start=window_start, window_end=window_end, historic_years=historic_years, layer=layer, agg_window=w_agg, agg_history=h_agg, comparison="diff", output=None, match_raster=None, farm_name=FARM_NAME, ) print(MODEL_PATH) print( f"n_clicks: {n_clicks}\n" + f"window_start: {window_start}\n" + f"window_end: {window_end}\n" + f"historic_years: {historic_years}\n" + f"layer: {layer}\n" + f"agg_window: {w_agg}\n" + f"agg_history: {h_agg}\n" + "comparison: 'diff'\n" + f"output: {None}\n" + f"match_raster: {None}\n" ) print(files) files = { i: [ " ".join(files[i].split("/")[-1].split(".")[0].split("-")).capitalize(), files[i], ] for i in files } print(files) options = [{"label": files[i][0], "value": files[i][1]} for i in files] return options @dashapp.callback( Output("graph", "figure"), Input("visualisation-select", "value"), Input("platter-dropdown", "value"), Input("generate-button", "n_clicks"), ) def change_colorscale(file, palette, n_clicks): """Display the selected visualisation and change the colorscale of the visualisation. Parameters ---------- file : str path to the visualisation file palette : str name of the colorscale to use Returns ------- fig : plotly.graph_objects.Figure plotly figure object """ if n_clicks == 0 or n_clicks is None or file is None: raise PreventUpdate band1, lons_a, lats_a = open_image(file) # Get the second dimension of the lons lats = lats_a[:, 0] lons = lons_a[0, :] if "quantile" in file: value_type = "Percentile" else: value_type = "SM" print(lons.shape, lons) print(lats.shape, lats) print(band1.shape, band1) print(file) fig = px.imshow(band1, x=lons, y=lats, color_continuous_scale=palette) fig.update( data=[ { "customdata": np.stack((band1, lats_a, lons_a), axis=-1), "hovertemplate": f"{value_type}" + ": %{customdata[0]}
" + "Lat: %{customdata[1]}
" + "Lon: %{customdata[2]}
" + "", } ] ) print("Render successful") return fig # ============================================================================== # Main # ============================================================================== if __name__ == "__main__": # Load Configs parser = argparse.ArgumentParser( description="Download rainfall data from Google Earth Engine for a range of dates.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument( "-i", "--input", help="Absolute or relative path to the netcdf data directory for each farm. Should be in this format: '/path/to/farm/{}/soilwatermodel'", default=os.path.join(os.path.expanduser("~"), "Data/results_default/{}"), ) parser.add_argument( "-d", "--debug", help="Debug mode as True or False. Default is True.", default=True, ) args = parser.parse_args() INPUT = args.input try: # dashapp.run_server(debug=args.debug) app.run(debug=args.debug) except Exception as e: vprint( 0, V, V_IGNORE, Error="Failed to execute the main function:", ErrorMessage=e, ) traceback.print_exc() raise e