run.py 9.25 KB
import random
import datetime
import StringIO
from math import sqrt

from os import listdir, environ
from os.path import isfile, join, abspath, dirname

import csv
from pprint import pprint
from csv import writer as csv_writer
from yaml import load as yaml_load
from flask import Flask
from flask import redirect, url_for, send_from_directory, abort
from flask import request
from jinja2 import Environment, FileSystemLoader
from netCDF4 import Dataset

# from model.Config import Config
# from model.News import NewsCollection, News


# PATH RELATIVITY #############################################################

THIS_DIRECTORY = dirname(abspath(__file__))


def get_path(relative_path):
    return abspath(join(THIS_DIRECTORY, relative_path))


# COLLECT GLOBAL INFORMATION FROM SOURCES #####################################

# VERSION
with open(get_path('../VERSION'), 'r') as version_file:
    version = version_file.read().strip()

# CONFIG
with open(get_path('../config.yml'), 'r') as config_file:
    config = yaml_load(config_file.read())


# SETUP FLASK ENGINE ##########################################################

app = Flask(__name__, root_path=THIS_DIRECTORY)
app.debug = environ.get('DEBUG') == 'true'


# SETUP JINJA2 TEMPLATE ENGINE ################################################

def static_global(filename):
    return url_for('static', filename=filename)


def shuffle_filter(seq):
    """
    This shuffles the sequence it is applied to.
    'tis a failure of jinja2 to not provide a shuffle filter by default.
    """
    try:
        result = list(seq)
        random.shuffle(result)
        return result
    except:
        return seq


def markdown_filter(value, nl2br=False, p=True):
    """
    nl2br: set to True to replace line breaks with <br> tags
    p: set to False to remove the enclosing <p></p> tags
    """
    from markdown import markdown
    from markdown.extensions.nl2br import Nl2BrExtension
    from markdown.extensions.abbr import AbbrExtension
    extensions = [AbbrExtension()]
    if nl2br is True:
        extensions.append(Nl2BrExtension())
    markdowned = markdown(value, output_format='html5', extensions=extensions)
    if p is False:
        markdowned = markdowned.replace(r"<p>", "").replace(r"</p>", "")
    return markdowned


tpl_engine = Environment(loader=FileSystemLoader([get_path('view')]),
                         trim_blocks=True,
                         lstrip_blocks=True)

tpl_engine.globals.update(
    url_for=url_for,
    static=static_global,
)

tpl_engine.filters['markdown'] = markdown_filter
tpl_engine.filters['md'] = markdown_filter
tpl_engine.filters['shuffle'] = shuffle_filter

tpl_global_vars = {
    'request': request,
    'version': version,
    'config': config,
    'now': datetime.datetime.now(),
}


# HELPERS #####################################################################

def render_view(view, context=None):
    """
    A simple helper to render [view] template with [context] vars.
    It automatically adds the global template vars defined above, too.
    It returns a string, usually the HTML contents to display.
    """
    context = {} if context is None else context
    return tpl_engine.get_template(view).render(
        dict(tpl_global_vars.items() + context.items())
    )


# def render_page(page, title="My Page", context=None):
#     """
#     A simple helper to render the md_page.html template with [context] vars &
#     the additional contents of `page/[page].md` in the `md_page` variable.
#     It automagically adds the global template vars defined above, too.
#     It returns a string, usually the HTML contents to display.
#     """
#     if context is None:
#         context = {}
#     context['title'] = title
#     context['md_page'] = ''
#     with file(get_path('page/%s.md' % page)) as f:
#         context['md_page'] = f.read()
#     return tpl_engine.get_template('md_page.html').render(
#         dict(tpl_global_vars.items() + context.items())
#     )

def datetime_from_list(time_list):
    # Day Of Year starts at 0, but for our datetime parser it starts at 1
    doy = '{:03d}'.format(int(''.join(time_list[4:7])) + 1)
    return datetime.datetime.strptime(
        "%s%s%s" % (''.join(time_list[0:4]), doy, ''.join(time_list[7:])),
        "%Y%j%H%M%S%f"
    )


# ROUTING #####################################################################

@app.route('/favicon.ico')
def favicon():
    return send_from_directory(
        join(app.root_path, 'static', 'img'),
        'favicon.ico', mimetype='image/vnd.microsoft.icon'
    )


@app.route("/")
@app.route("/home.html")
@app.route("/index.html")
def home():
    return render_view('home.html.jinja2', {
        'sources': config['sources'],
        'planets': [s for s in config['sources'] if s['type'] == 'planet'],
        'probes':  [s for s in config['sources'] if s['type'] == 'probe'],
        'comets':  [s for s in config['sources'] if s['type'] == 'comet'],
    })


@app.route("/inspect")
def analyze_cdf():
    cdf_to_inspect = get_path("../res/dummy.nc")
    cdf_to_inspect = get_path("../res/dummy_jupiter_coordinates.nc")

    si = StringIO.StringIO()
    cw = csv.DictWriter(si, fieldnames=['Name', 'Shape', 'Length'])
    cw.writeheader()

    # Time, StartTime, StopTime, V, B, N, T, Delta_angle, P_dyn, QualityFlag
    cdf_handle = Dataset(cdf_to_inspect, "r", format="NETCDF4")
    for variable in cdf_handle.variables:
        v = cdf_handle.variables[variable]
        cw.writerow({
            'Name': variable,
            'Shape': v.shape,
            'Length': v.size,
        })
    cdf_handle.close()

    return si.getvalue()


@app.route("/test.csv")
def get_csv():
    si = StringIO.StringIO()
    cw = csv_writer(si)

    # Time, StartTime, StopTime, V, B, N, T, Delta_angle, P_dyn, QualityFlag
    cdf_handle = Dataset(get_path("../res/dummy.nc"), "r", format="NETCDF4")
    # YYYY DOY HH MM SS .ms
    times = cdf_handle.variables['Time']
    data_v = cdf_handle.variables['V']
    data_b = cdf_handle.variables['B']
    data_t = cdf_handle.variables['T']
    data_n = cdf_handle.variables['N']
    data_p = cdf_handle.variables['P_dyn']
    data_a = cdf_handle.variables['Delta_angle']
    cw.writerow((
        'time',
        'vrad', 'vtan', 'vlen',
        'magn', 'temp', 'pdyn', 'dens', 'angl'
    ))
    for time, datum_v, datum_b, datum_t, datum_p, datum_n, datum_a in \
            zip(times, data_v, data_b, data_t, data_n, data_p, data_a):
        vrad = datum_v[0]
        vtan = datum_v[1]
        cw.writerow((
            datetime_from_list(time).strftime("%Y-%m-%dT%H:%M:%S+00:00"),
            vrad, vtan, sqrt(vrad * vrad + vtan * vtan),
            datum_b, datum_t, datum_n, datum_p, datum_a
        ))
    cdf_handle.close()

    return si.getvalue()


@app.route("/<orbiter>_data.csv")
def get_orbiter_csv(orbiter):
    # http://cdpp1.cesr.fr/BASE/DDService/getDataUrl.php?dataSet=tao_ros_sw&StartTime=2014-02-23T10:00&StopTime=2016-02-24T23:59
    # Process input parameters
    if orbiter not in config['models']:
        abort(400, "Invalid orbiter '%s'." % orbiter)
    date_fmt = "%Y-%m-%dT%H:%M:%S"
    started_at = request.args.get('started_at')
    try:
        started_at = datetime.datetime.strptime(started_at, date_fmt)
    except:
        abort(400, "Invalid started_at parameter : '%s'." % started_at)
    stopped_at = request.args.get('stopped_at')
    try:
        stopped_at = datetime.datetime.strptime(stopped_at, date_fmt)
    except:
        abort(400, "Invalid stopped_at parameter : '%s'." % stopped_at)

    # Grab the list of netCDF files from Myriam's API todo
    #

    si = StringIO.StringIO()
    cw = csv_writer(si)

    # Time, StartTime, StopTime, V, B, N, T, Delta_angle, P_dyn, QualityFlag
    cdf_handle = Dataset(get_path("../res/dummy.nc"), "r", format="NETCDF4")

    # YYYY DOY HH MM SS .ms
    times = cdf_handle.variables['Time']
    data_v = cdf_handle.variables['V']
    data_b = cdf_handle.variables['B']
    data_p = cdf_handle.variables['P_dyn']
    cw.writerow((
        'time',
        'vrad', 'vtan', 'vlen',
        'magn', 'pdyn'
    ))
    for time, datum_v, datum_b, datum_p in zip(times, data_v, data_b, data_p):
        vrad = datum_v[0]
        vtan = datum_v[1]
        cw.writerow((
            datetime_from_list(time).strftime("%Y-%m-%dT%H:%M:%S+00:00"),
            vrad, vtan, sqrt(vrad * vrad + vtan * vtan),
            datum_b, datum_p
        ))
    cdf_handle.close()

    return si.getvalue()


@app.route("/astral_coordinates.csv")
def get_astral_coordinates_csv():
    si = StringIO.StringIO()
    cw = csv_writer(si)

    # Time, StartTime, StopTime, XYZ_HCI, XYZ_IAU_SUN, XYZ_HEE
    cdf_handle = Dataset(get_path("../res/dummy_jupiter_coordinates.nc"), "r", format="NETCDF4")
    times = cdf_handle.variables['Time']
    data_xyz_hci = cdf_handle.variables['XYZ_HCI']
    cw.writerow(('time', 'x_hci', 'y_hci'))
    for time, datum_xyz_hci in zip(times, data_xyz_hci):
        cw.writerow((
            datetime_from_list(time).strftime("%Y-%m-%dT%H:%M:%S+00:00"),
            datum_xyz_hci[0], datum_xyz_hci[1]
        ))
    cdf_handle.close()

    return si.getvalue()

# MAIN ########################################################################

if __name__ == "__main__":
    # Debug mode on, as the production server does not use this.
    extra_files = [get_path('../config.yml')]
    app.run(debug=True, extra_files=extra_files)