Skip to content
Snippets Groups Projects
Verified Commit 23259af5 authored by David Hoese's avatar David Hoese
Browse files

Add basic instrument status API

parent 6c3c3e28
No related branches found
No related tags found
No related merge requests found
# MetObs API Application
This is a flask application that is served at `metobs.ssec.wisc.edu/api`.
See https://metobs.ssec.wisc.edu/api` for documentation.
## Developer Installation
Install the package in "development" mode:
```bash
pip install gunicorn
pip install -e .
```
Run a local flask server:
```bash
gunicorn --reload --reload-extra metobsapi/templates/ metobsapi.server:app
```
Open browser to ``localhost:8000/api``.
#might have to restart from scratch again
import os
import json as builtin_json
import logging
from urllib.request import urlopen, URLError
import pandas as pd
from flask import Flask, render_template, request, jsonify, Response
from flask import Flask, render_template, request, jsonify
from flask_cors import CORS
from flask_json import FlaskJSON
......@@ -11,6 +11,7 @@ from enum import Enum
from metobsapi import data_api, files_api
from metobsapi.util import file_responses, data_responses
LOG = logging.getLogger(__name__)
app = Flask(__name__)
......@@ -28,6 +29,7 @@ def enum_encoder(o):
if isinstance(o, Enum):
return o.value
# Allow for cross-domain access to the API using CORS
CORS(app, resources=r'/api/*', allow_headers='Content-Type')
......@@ -49,7 +51,7 @@ def files_index():
@app.route('/api/data')
def data_index():
"""Data API Documentation"""
return render_template('data_index.html')
return render_template('data_index.html')
@app.errorhandler(404)
......@@ -104,6 +106,70 @@ def get_archive_info():
'sites': file_responses.ARCHIVE_INFO,
})
@app.route('/api/status', methods=['GET'])
def status_index():
return render_template('status_index.html')
@app.route('/api/status/<site>/<inst>.<fmt>', methods=['GET'])
@app.route('/api/status/<site>/<inst>', methods=['GET'])
@app.route('/api/status/<site>.<fmt>', methods=['GET'])
@app.route('/api/status/<site>', methods=['GET'])
def get_instrument_status(site, inst=None, fmt=None):
"""See `/api/status/` for more information."""
# defaults:
response = {
"name": site if inst is None else inst,
"short_name": "",
"long_name": "",
"status_code": 255,
"status_message": "",
}
if fmt is None:
fmt = "html"
if fmt not in ['html', 'json']:
return render_template('400.html', format=fmt), 400
if inst is None:
json_subpath = os.path.join(site, "status.json")
else:
json_subpath = os.path.join(site, inst, "status.json")
# try to load the JSON file from the archive
if not os.path.exists(app.config.get("ARCHIVE_ROOT")):
LOG.warning("Using URL request for status JSON, not meant for operational use")
# we aren't on a system with the archive available, fall back to URL
# loads directly to the archive
base_url = app.config.get("ARCHIVE_URL")
json_url = os.path.join(base_url, json_subpath)
try:
json_str = urlopen(json_url).read()
except URLError:
response['status_message'] = "Could not retrieve configured status: {}".format(json_url)
json_str = None
else:
base_path = app.config.get("ARCHIVE_ROOT")
json_path = os.path.join(base_path, json_subpath)
try:
json_str = open(json_path, 'r').read()
except FileNotFoundError:
response['status_message'] = "No status information found."
json_str = None
if json_str is None:
# exit out early, we don't know what this is
return jsonify(response)
json_dict = builtin_json.loads(json_str)
response.update(json_dict)
if fmt == 'json':
return jsonify(response)
else:
return "<br>".join("{}: {}".format(k, v) for k, v in sorted(response.items()))
if __name__ == '__main__':
app.debug = True
app.run('0.0.0.0', threaded=True)
......@@ -63,7 +63,7 @@
</h4>
<br>
<p>
The files are provided courtesy of SSEC/AOSS UW-Madison. Clients cauing issues due to size or
The files are provided courtesy of SSEC/AOSS UW-Madison. Clients causing issues due to size or
quantity of queries may be blocked from further access until the issue
is resolved
</p>
......
......@@ -54,7 +54,16 @@
files using the times, and file stream names that they desire. The user will get a CSV or JSON with file information
or a bash/bat script that will download the file to the directory of choice. For more information, click the link above.
</p>
</div>
<h2 id="#Instrument Status API Documentation">
<a href="{{ url_for('status_index')}}">
Instrument Status API Documentation
</a>
</h2>
<p>
This API returns the current status of the instrument site or instrument specified.
</p>
</div>
</div>
<!-- <script type="text/javascript" src="index.js"></script> -->
......
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Instrument Status API</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='sidebar.css') }}">
</head>
<body>
<div class="container-fluid">
<div class="row" id="row1">
<nav class="col-md-2" id="sidebar-wrapper">
<ul id="sidebar" class="nav nav-stacked fixed" style='position: fixed'>
<li>
<a href="#Instrument Status API">
Instrument Status API
</a>
</li>
<li>
<a href="#Status Information">
Products Available
</a>
</li>
<li>
<a href="#Query Format">
Query Format
</a>
</li>
<li>
<a href="#Status Code Meanings">
Status Code Meanings
</a>
</li>
<li>
<a href="#Example Queries">
Example Queries
</a>
</li>
</ul>
</nav>
<div class="col-md-8" style='border-left: solid;border-color: #A0A0A0;'>
<h1 id='Instrument Status API'>
Instrument Status API
</h1>
<div class="panel panel-default">
<div style='padding-left: 10px; padding-bottom: 10px; padding-top: 5px'>
<h4>
Note
</h4>
<br>
<p>
The files are provided courtesy of SSEC/AOSS UW-Madison. Clients causing issues due to size or
quantity of queries may be blocked from further access until the issue
is resolved
</p>
<br>
<a href='http://www.ssec.wisc.edu/disclaimer.html'>
Disclaimer
</a>
</div>
</div>
<div>
<h2 id='Products available'>
Products Available
</h2>
<p>
Instrument and site status is technically available for any site/instrument
archived as part of this system. However, some may not be configured on the
backend and will return missing or unknown information.
</p>
</div>
<div>
<h2 id='Query Format'>
Query Format
</h2>
<div id='base url'>
<h3>
Base URL
</h3>
<p>
The base URL of a status query specifies the requested site, instrument (optional),
and file format (optional) to return:
</p>
<p style='text-indent: 50px'>
<a href='http://metobs.ssec.wisc.edu/api/status>http://metobs.ssec.wisc.edu/api/status</a>/&#60site&#62/&#60inst&#62.&#60fmt&#62
</p>
<p>
Valid values for <cite style='font-style: italic;'>fmt</cite> are:
</p>
<pre style='padding: 0px; padding-top: 15px; background: none; border: none'>
html (default if not provided)
json
</pre>
<p>
Valid values for <cite style='font-style: italic;'>site</cite> are:
</p>
<pre style='padding: 0px; padding-top: 15px; background: none; border: none'>
aoss
mendota
</pre>
<p>
Valid values for <cite style='font-style: italic;'>inst</cite> at the "aoss" site are:
</p>
<pre style='padding: 0px; padding-top: 15px; background: none; border: none'>
tower
ceilo
</pre>
<p>
Valid values for <cite style='font-style: italic;'>inst</cite> at the "mendota" site are:
</p>
<pre style='padding: 0px; padding-top: 15px; background: none; border: none'>
buoy
</pre>
</div>
<div id='Response Format'>
<h3>
Response Format
</h3>
<p>
The response is a key-value mapping (dictionary) with the following keys:
<ul>
<li>name: short computer-friendly name</li>
<li>short_name: Short human-friendly name</li>
<li>long_name: Long human-friendly name. For instruments this may include site name.</li>
<li>status_code: Integer code for current instrument status (see below)</li>
<li>status_message: Description and reason for current status</li>
</ul>
</p>
</div>
</div>
<!-- Stream Quantifiers div -->
<div>
<h2 id='Status Code Meanings'>
Status Code Meanings
</h2>
<ul>
<!-- Providing data, maybe not all of it -->
<li>0: Nominal operation</li>
<li>1: Limited functionality but data being produced</li>
<li>2-7: reserved </li>
<!-- Not providing data, but expected down time -->
<li>8: Out for the season (acceptable/expected out-of-operation)</li>
<li>9: Hardware maintenance (acceptable/expected)</li>
<li>10: Software maintenance (acceptable/expected)</li>
<li>11-15: reserved</li>
<!-- Not providing data from unexpected failures -->
<li>16: Hardware failure (unexpected)</li>
<li>17: Software failure including processing hardware failures (unexpected)</li>
<li>18-254: reserved</li>
<li>255: Unknown</li>
</ul>
</div>
<!-- Example queries -->
<div>
<h2 id='Example Queries'>
Example Queries
</h2>
<ul>
</ul>
</div>
</div>
</div>
</div>
<!-- <script type="text/javascript" src="index.js"></script> -->
</body>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment