Skip to content

Commit

Permalink
feat: add VTEC list by state/wfo option
Browse files Browse the repository at this point in the history
  • Loading branch information
akrherz committed Nov 7, 2023
1 parent 901fbff commit e1b1574
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 58 deletions.
45 changes: 30 additions & 15 deletions htdocs/json/vtec_events.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"""Listing of VTEC events for a WFO and year"""
import datetime
import json
from io import BytesIO, StringIO

import pandas as pd
from pyiem.util import get_dbconnc, html_escape, utc
from pyiem.webutil import iemapp
from pymemcache.client import Client

ISO9660 = "%Y-%m-%dT%H:%M:%SZ"
EXL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"


def run(wfo, year, phenomena, significance, combo):
def get_res(wfo, year, phenomena, significance, combo):
"""Generate a report of VTEC ETNs used for a WFO and year
Args:
Expand Down Expand Up @@ -95,7 +97,7 @@ def run(wfo, year, phenomena, significance, combo):
)
)
pgconn.close()
return json.dumps(res)
return res


@iemapp()
Expand All @@ -119,21 +121,34 @@ def application(environ, start_response):
cb = environ.get("callback")
combo = int(environ.get("combo", 0))

mckey = (
f"/json/vtec_events/{wfo}/{year}/{phenomena}/{significance}/{combo}"
)
mc = Client("iem-memcached:11211")
res = mc.get(mckey)
if not res:
res = run(wfo, year, phenomena, significance, combo)
mc.set(mckey, res, 60)
else:
res = res.decode("utf-8")
mc.close()
fmt = environ.get("fmt", "json")
res = get_res(wfo, year, phenomena, significance, combo)

if fmt == "xlsx":
fn = f"vtec_{wfo}_{year}_{phenomena}_{significance}.xlsx"
headers = [
("Content-type", EXL),
("Content-disposition", f"attachment; Filename={fn}"),
]
start_response("200 OK", headers)
bio = BytesIO()
pd.DataFrame(res["events"]).to_excel(bio, index=False)
return [bio.getvalue()]
if fmt == "csv":
fn = f"vtec_{wfo}_{year}_{phenomena}_{significance}.csv"
headers = [
("Content-type", "application/octet-stream"),
("Content-disposition", f"attachment; Filename={fn}"),
]
start_response("200 OK", headers)
bio = StringIO()
pd.DataFrame(res["events"]).to_csv(bio, index=False)
return [bio.getvalue().encode("utf-8")]

res = json.dumps(res)
if cb is not None:
res = f"{html_escape(cb)}({res})"

headers = [("Content-type", "application/json")]
start_response("200 OK", headers)
return [res.encode("utf-8")]
return [res.encode("ascii")]
45 changes: 28 additions & 17 deletions htdocs/json/vtec_events_bystate.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Listing of VTEC events for state and year"""
import json
from io import BytesIO, StringIO

import pandas as pd
from pyiem.util import get_dbconnc, html_escape
from pyiem.webutil import iemapp
from pymemcache.client import Client

ISO9660 = "%Y-%m-%dT%H:%M:%SZ"
EXL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"


def run(state, year, phenomena, significance):
def get_res(state, year, phenomena, significance):
"""Generate a report of VTEC ETNs used for a WFO and year
Args:
Expand Down Expand Up @@ -82,7 +84,7 @@ def run(state, year, phenomena, significance):
)
)
pgconn.close()
return json.dumps(res)
return res


@iemapp()
Expand All @@ -93,22 +95,31 @@ def application(environ, start_response):
phenomena = environ.get("phenomena", "__")[:2]
significance = environ.get("significance", "_")[:1]
cb = environ.get("callback")
fmt = environ.get("fmt", "json")
res = get_res(state, year, phenomena, significance)

mckey = "/json/vtec_events_bystate/%s/%s/%s/%s" % (
state,
year,
phenomena,
significance,
)
mc = Client("iem-memcached:11211")
res = mc.get(mckey)
if not res:
res = run(state, year, phenomena, significance)
mc.set(mckey, res, 60)
else:
res = res.decode("utf-8")
mc.close()
if fmt == "xlsx":
fn = f"vtec_{state}_{year}_{phenomena}_{significance}.xlsx"
headers = [
("Content-type", EXL),
("Content-disposition", f"attachment; Filename={fn}"),
]
start_response("200 OK", headers)
bio = BytesIO()
pd.DataFrame(res["events"]).to_excel(bio, index=False)
return [bio.getvalue()]
if fmt == "csv":
fn = f"vtec_{state}_{year}_{phenomena}_{significance}.csv"
headers = [
("Content-type", "application/octet-stream"),
("Content-disposition", f"attachment; Filename={fn}"),
]
start_response("200 OK", headers)
bio = StringIO()
pd.DataFrame(res["events"]).to_csv(bio, index=False)
return [bio.getvalue().encode("utf-8")]

res = json.dumps(res)
if cb is not None:
res = f"{html_escape(cb)}({res})"

Expand Down
116 changes: 114 additions & 2 deletions htdocs/vtec/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ let mapwidget1 = null;
let mapwidget2 = null;
let table1 = null;
let table2 = null;
let table3 = null;
let table2IsByPoint = true;
let hashlinkUGC = null;
let edate = null;
Expand All @@ -13,6 +14,8 @@ let sdate1 = null;
const BACKEND_EVENTS_BYPOINT = '/json/vtec_events_bypoint.py';
const BACKEND_EVENTS_BYUGC = '/json/vtec_events_byugc.py';
const BACKEND_SBW_BYPOINT = '/json/sbw_by_point.py';
const BACKEND_EVENTS = "/json/vtec_events.py";
const BACKEND_EVENTS_BYSTATE = "/json/vtec_events_bystate.py";

const states = [["AL", "Alabama"], ["AK", "Alaska"], ["AZ", "Arizona"],
["AR", "Arkansas"], ["CA", "California"], ["CO", "Colorado"],
Expand Down Expand Up @@ -156,12 +159,46 @@ function updateTable2ByPoint(){
});
}

function updateTable3(){
// get currently selected by3 radio button
const by = $("input[name='by3']:checked").val();
const datum = (by == "state") ? stateSelect.val() : $("#wfo3").val();
const year = $("#year3").val();
const ph = $("#ph3").val();
const sig = $("#sig3").val();
window.location.href = `#list/${by}/${datum}/${year}/${ph}/${sig}`;

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
DOM text
is reinterpreted as HTML without escaping meta-characters.
$("#table3title").text(`Events for ${by} ${datum} in ${year}`);
// Do what we need to for table 3
$.ajax({
data: {
wfo: $("#wfo3").val(),
state: stateSelect.val(),
year: year,
phenomena: ph,
significance: sig
},
url: (by == "wfo") ? BACKEND_EVENTS: BACKEND_EVENTS_BYSTATE,
dataType: "json",
method: "GET",
success: (data) => {
table3.clear();
$.map(data.events, (row) => {
const uri = `<a href="${row.uri}" target="_blank">${row.phenomena}.${row.significance}.${row.eventid}</a>`;
table3.row.add(
[uri, row.wfo, row.locations, row.issue, row.expire]);
});
table3.draw();
}
});
}


function buildUI(){
// Export Buttons
$(".iemtool").click(function(){ // this
const btn = $(this);
let url = BACKEND_SBW_BYPOINT;
const params = {
var params = {
fmt: (btn.data("opt") == "csv") ? "csv" : "xlsx",
lat: $("#lat").val(),
lon: $("#lon").val(),
Expand All @@ -179,6 +216,18 @@ function buildUI(){
params.lat = $("#lat2").val();
}
}
if (btn.data("table") == "3"){
const by = $("input[name='by3']:checked").val();
url = (by == "state") ? BACKEND_EVENTS_BYSTATE: BACKEND_EVENTS;
params = {
fmt: (btn.data("opt") == "csv") ? "csv" : "xlsx",
wfo: $("#wfo3").val(),
state: stateSelect.val(),
year: $("#year3").val(),
phenomena: $("#ph3").val(),
significance: $("#sig3").val()
};
}
window.location = `${url}?${$.param(params)}`;
});
// Tables
Expand All @@ -192,6 +241,11 @@ function buildUI(){
"emptyTable": "Drag marker on map or select UGC to auto-populate this table"
}
});
table3 = $("#table3").DataTable({
"language": {
"emptyTable": "Select options to auto-populate this table"
}
});
// Date pickers
sdate = $("input[name='sdate']").datepicker({
dateFormat:"mm/dd/yy",
Expand Down Expand Up @@ -300,7 +354,46 @@ function buildUI(){
mapwidget2.marker.setPosition(latlng);
updateMarkerPosition2(lo, la);
});

$("#button3").click(() => {
updateTable3();
});
// Populate wfos select with iemdata.wfos data
const wfos = $.map(iemdata.wfos, (obj) => {
const ee = {};
ee.id = obj[0];
ee.text = `[${obj[0]}] ${obj[1]}`;
return ee;
});
$("select[name='wfo']").select2({
placeholder: "Select a WFO",
data: wfos
});
const ph = $.map(iemdata.vtec_phenomena, (obj) => {
const ee = {};
ee.id = obj[0];
ee.text = obj[1];
return ee;
});
$("select[name='ph']").select2({
placeholder: "Select a Phenomena",
data: ph
});
const sig = $.map(iemdata.vtec_significance, (obj) => {
const ee = {};
ee.id = obj[0];
ee.text = obj[1];
return ee;
});
$("select[name='sig']").select2({
placeholder: "Select a Phenomena",
data: sig
});
// populate year3 select with values from 1986 to current year
const year3 = $("select[name='year']");
const currentYear = new Date().getFullYear();
for (let i = 1986; i <= currentYear; i++){
year3.append(new Option(i, i, false, false));
}
};

function _load() {
Expand Down Expand Up @@ -340,6 +433,25 @@ function _load() {
updateMarkerPosition2(default_lon, default_lat);
}
}
if (tokens2.length == 6){
const aTag = $("a[name='list']");
$('html,body').animate({scrollTop: aTag.offset().top},'slow');
const by = tokens2[1];
const datum = tokens2[2];
const year = tokens2[3];
const ph = tokens2[4];
const sig = tokens2[5];
$("input[name='by3'][value='"+by+"']").prop("checked", true);
$("#year3").val(year);
$("#ph3").val(ph);
$("#sig3").val(sig);
if (by == "state"){
stateSelect.val(datum).trigger("change");
} else {
$("#wfo3").val(datum);
}
updateTable3();
}
}

mapwidget1 = new MapMarkerWidget("map", default_lon, default_lat);
Expand Down
50 changes: 47 additions & 3 deletions htdocs/vtec/search.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
<script src="/vendor/jquery-datatables/1.10.20/datatables.min.js"></script>
<script src="/vendor/jquery-ui/1.11.4/jquery-ui.js"></script>
<script src="/vendor/select2/4.1.0rc0/select2.min.js"></script>
<script type="text/javascript" src="search.js?v=5"></script>
<script type="text/javascript" src="wfos.js"></script>
<script type="text/javascript" src="search.js?v=6"></script>
<script src="https://maps.googleapis.com/maps/api/js?key={$gmapskey}&callback=_load" type="text/javascript"></script>
EOF;
$t->headextra = <<<EOF
Expand All @@ -36,8 +37,9 @@
<ul>
<li><a href="#bypoint">1. Search for Storm Based Warnings by Point</a></li>
<li><a href="#byugc">2. Search of Watch/Warning/Advisories by County/Zone or by Point</a></li>
<li><a href="#list">3. List Watch/Warning/Advisories by State/WFO by Year</a></li>
</ul>
<h3><a name="bypoint">1.</a> Search for Storm Based Warnings by Point</h3>
<br />The official warned area for some products the NWS issues is a polygon.
Expand Down Expand Up @@ -138,6 +140,48 @@
</div>
</div><!-- ./row -->
</form><!-- ./form2 -->
<br clear="all" />
<h3><a name="list">3.</a> List NWS Watch/Warning/Advisories by State/WFO by Year</h3>
<br />
<p>This section generates a simple list of NWS Watch, Warning, and Advisories
by state and year.</p>
<br />
<form id="form3">
<div class="row">
<div class="col-md-4">
<input type="radio" name="by3" value="state" checked="checked" id="bystate"/>
<label for="bystate">Select By State</label>
<br /><select name="state" style="width: 100%" id="state3"></select></p>
<p><input type="radio" name="by3" value="wfo" id="bywfo"/>
<label for="bywfo">Select By WFO</label>
<br /><select name="wfo" style="width: 100%" id="wfo3"></select></p>
<p><label for="ph3">Select VTEC Phenomena:</label>
<select name="ph" style="width: 100%" id="ph3"></select></p>
<p><label for="sig3">Select VTEC Significance:</label>
<select name="sig" style="width: 100%" id="sig3"></select></p>
<p><label for="year3">Select Year:</label>
<select name="year" style="width: 100%" id="year3"></select></p>
<br /><button type="button" class="btn btn-default" id="button3">Update Table</button>
</div>
<div class="col-md-8">
<h4 id="table3title"></h4>
<button type="button" data-table="3" data-opt="excel" class="btn btn-default iemtool"><i class="fa fa-download"></i> Export to Excel...</button>
<button type="button" data-table="3" data-opt="csv" class="btn btn-default iemtool"><i class="fa fa-download"></i> Export to CSV...</button>
<table id="table3" data-order='[[ 3, "desc" ]]'>
<thead>
<tr><th>Event</th><th>WFO</th><th>Locations</th><th>Issued</th>
<th>Expired</th></tr></thead>
</table>
</div>
</div><!-- ./row -->
</form><!-- ./form3 -->
EOF;
$t->render('full.phtml');
Loading

0 comments on commit e1b1574

Please sign in to comment.