commenting out console.log
[wrfxctrl.git] / wrfxctrl.py
blob3b6c108b3c751a10cd45c066311d86bbab86f419
1 # Copyright (C) 2013-2016 Martin Vejmelka, UC Denver
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 # of the Software, and to permit persons to whom the Software is furnished to do
8 # so, subject to the following conditions:
10 # The above copyright notice and this permission notice shall be included in all
11 # copies or substantial portions of the Software.
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
15 # A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
16 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 from __future__ import absolute_import
22 from __future__ import print_function
23 from cluster import Cluster
24 from simulation import create_simulation, get_simulation_state, cancel_simulation, delete_simulation, load_simulations
25 from utils import Dict, to_esmf, to_utc, load_profiles, load_sys_cfg, parse_kml
26 from flask import Flask, render_template, request, redirect, make_response, url_for
27 import json
28 from datetime import datetime, timedelta
29 import random
30 import string
31 import os.path as osp
32 from functools import wraps, update_wrapper
33 import sys
34 from cleanup import cleanup_delete, cleanup_cancel
36 # global objects tracking state
37 cluster = None
38 simulations = {}
39 profiles = None
41 # conf params and state
42 conf = load_sys_cfg()
43 sims_path = conf['sims_path']
44 simulations = load_simulations(sims_path)
46 if 'root' in conf:
47 root = osp.join('/',conf['root'])
48 else:
49 root = osp.join('/',''.join(random.choice(string.ascii_lowercase) for i in range(5)))
50 host = conf['host']
51 debug = conf['debug'] in ['T', 'True', 't', 'true']
52 port = conf['port']
53 urls = {
54 'submit': osp.join(root, 'submit'),
55 'welcome': osp.join(root, 'start'),
56 'overview': osp.join(root, 'overview'),
58 print('Welcome page is http://%s:%s%s' % (host, port, urls['welcome']) )
60 app = Flask(__name__)
63 # lifted from: http://arusahni.net/blog/2014/03/flask-nocache.html
64 def nocache(view):
65 @wraps(view)
66 def no_cache(*args, **kwargs):
67 response = make_response(view(*args, **kwargs))
68 response.headers['Last-Modified'] = datetime.now()
69 response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
70 response.headers['Pragma'] = 'no-cache'
71 response.headers['Expires'] = '-1'
72 return response
74 return update_wrapper(no_cache, view)
76 # @app.route(root)
77 @app.route(urls['welcome'])
78 @nocache
79 def welcome():
80 return render_template('welcome.html', cluster=cluster, urls=urls)
83 @app.route(urls['submit'], methods=['GET', 'POST'])
84 def build():
85 if request.method == 'GET':
86 # it's a get so let's build a fire simulation
87 return render_template('build.html', profiles=list(profiles.values()), urls=urls)
88 elif request.method == 'POST':
89 # it's a POST so initiate a simulation
90 # dictionary values set in the html <select name="KEY" class="ui dropdown" id="KEY">
91 sim_cfg = request.form.copy()
92 print('values returned by build page:')
93 print(json.dumps(sim_cfg, indent=4, separators=(',', ': ')))
94 sim_cfg['profile'] = profiles[sim_cfg['profile']]
95 sim_info = create_simulation(sim_cfg, conf, cluster)
96 sim_id = sim_info['id']
97 simulations[sim_id] = sim_info
98 print('sim_info:')
99 print(json.dumps(sim_info, indent=4, separators=(',', ': ')))
100 f = osp.join(sims_path, sim_id + '.json')
101 json.dump(sim_info, open(f, 'w'), indent=4, separators=(',', ': '))
102 return redirect("/monitor/%s" % sim_id)
104 @app.route(osp.join(urls['submit'],'sat_data'), methods=['GET'])
105 def getSatData():
106 if request.method == 'GET':
107 with open('./sat_data/filteredSatData.json', 'r') as read_file:
108 # with open('./sat_data/filtered_hotspots_2021-03-03_1838.json', 'r') as read_file:
109 sat_data = json.load(read_file)
110 return sat_data
113 @app.route("/monitor/<sim_id>")
114 @nocache
115 def monitor(sim_id=None):
116 print('monitor {}'.format(sim_id))
117 return render_template('monitor.html', sim=simulations.get(sim_id, None), urls=urls)
120 @app.route(urls['overview'], methods=['GET', 'POST'])
121 @nocache
122 def overview():
123 if request.method == 'GET':
124 # reload, cleanup might delete jsons while webserver is running
125 simulations = load_simulations(sims_path)
126 deadline = to_esmf(datetime.now() - timedelta(seconds=5))
127 # only update stale & running simulations in overview
128 kk = list(simulations.keys())
129 for sim_id in kk:
130 sim = simulations[sim_id]
131 if sim['state']['wrf'] != 'complete':
132 last_upd = sim.get('last_updated', '2000-01-01_00:00:00')
133 if last_upd < deadline:
134 sim['state'] = get_simulation_state(sim['log_file'])
135 sim['last_updated'] = to_esmf(datetime.now())
136 f = osp.join(sims_path, sim_id + '.json')
137 if osp.isfile(f):
138 json.dump(sim, open(f,'w'), indent=4, separators=(',', ': '))
139 simulations[sim_id] = sim
140 else:
141 print('File %s no longer exists, deleting simulation' % f)
142 del simulations[sim_id]
143 return render_template('overview.html', simulations=simulations, urls=urls)
144 elif request.method == 'POST':
145 print('Values returned by overview page:')
146 sims_checked = request.form.getlist('sim_chk')
147 print(sims_checked)
148 for sim_id in sims_checked: # Only the simulation(s) checked in checkbox.
149 if 'RemoveB' in request.form:
150 print('Remove Sim: box checked= %s' % (sim_id))
151 cleanup_delete(sim_id)
152 else:
153 if 'CancelB' in request.form:
154 print('Cancel Sim: box checked= %s' % (sim_id))
155 cleanup_cancel(sim_id)
156 else:
157 print('Error-No button push detected: box checked= %s' % (sim_id))
158 simulations = load_simulations(sims_path)
159 return render_template('overview.html', simulations = simulations, urls=urls)
162 # JSON access to state
163 @app.route("/retrieve_log/<sim_id>")
164 def retrieve_log(sim_id=None):
165 sim_info = simulations.get(sim_id, None)
166 if sim_info is None:
167 return ""
168 else:
169 return open(sim_info['log_file']).read()
172 @app.route("/sim_info/<sim_id>")
173 def retrieve_sim_info(sim_id=None):
174 sim_info = simulations.get(sim_id, {}).copy()
175 return json.dumps(sim_info, indent=4, separators=(',', ': '))
178 @app.route("/get_state/<sim_id>")
179 def get_state(sim_id=None):
180 sim_info = simulations.get(sim_id, None)
181 if sim_info is None:
182 return "{}"
183 else:
184 sim_state = None
185 # always update during get_state()
186 if sim_info['state']['wrf'] != 'completed':
187 sim_state = get_simulation_state(sim_info['log_file'])
188 sim_info['state'] = sim_state
189 sim_info['last_updated'] = to_esmf(datetime.now())
190 f = osp.join('simulations', sim_id + '.json')
191 json.dump(sim_info, open(f, 'w'), indent=4, separators=(',', ': '))
192 return json.dumps(sim_state)
195 @app.route("/remove_sim/<sim_id>")
196 def remove_sim(sim_id=None):
197 if sim_id is not None:
198 delete_simulation(simulations(sim_id, conf))
199 del simulations[sim_id]
202 @app.route("/cancel_sim/<sim_id>")
203 def cancel_sim(sim_id=None):
204 if sim_id is not None:
205 cancel_simulation(simulations(sim_id, conf))
208 @app.route("/all_sims")
209 def get_all_sims():
210 print(json.dumps(simulations, indent=4, separators=(',', ': ')))
211 return json.dumps(simulations, indent=4, separators=(',', ': '))
213 @app.route("/upload_perimeter", methods=['POST'])
214 def upload_perimeter_file():
215 kml_content = request.files['file'].read()
216 data = parse_kml(kml_content, 'Polygon')
217 result = { 'data': data }
218 return json.dumps(result, indent=4, separators=(',', ':'))
220 @app.route("/upload_line", methods=['POST'])
221 def upload_line_file():
222 kml_content = request.files['file'].read()
223 data = parse_kml(kml_content, 'LineString')
224 result = { 'data': data }
225 return json.dumps(result, indent=4, separators=(',', ':'))
227 if __name__ == '__main__':
228 profiles = load_profiles()
229 cluster = Cluster(json.load(open('etc/cluster.json')))
230 sys.stdout.flush()
231 app.run(host=host, port=port, debug=debug)