Commit bc18b96cedce9e4dcbc8ed20a08c7888d6da2b74

Authored by Goutte
1 parent 3f0d5ea2

Implement first (working!) draft of NetCDF generation.

Still more to do, like the time and the orbits.
Showing 1 changed file with 125 additions and 18 deletions   Show diff stats
web/run.py
1   -import random
2   -import datetime
3 1 import StringIO
4   -from math import sqrt
5   -
6   -from os import listdir, environ, remove as removefile
7   -from os.path import isfile, join, abspath, dirname, isdir
8   -
9   -import csv
10   -import json
  2 +import datetime
  3 +import time
11 4 import gzip
  5 +import json
  6 +import logging
  7 +import random
12 8 import tarfile
13 9 import urllib
14   -import logging
15   -from pprint import pprint
16 10 from csv import writer as csv_writer
17   -from yaml import load as yaml_load
  11 +from math import sqrt
  12 +from os import environ, remove as removefile
  13 +from os.path import isfile, join, abspath, dirname
  14 +
18 15 from flask import Flask
19   -from flask import redirect, url_for, send_from_directory, abort as abort_flask
20 16 from flask import request
  17 +from flask import url_for, send_from_directory, abort as abort_flask
21 18 from jinja2 import Environment, FileSystemLoader
22 19 from netCDF4 import Dataset
23   -
  20 +from yaml import load as yaml_load
24 21  
25 22 # PATH RELATIVITY #############################################################
26 23  
... ... @@ -162,6 +159,13 @@ def render_view(view, context=None):
162 159 # )
163 160  
164 161  
  162 +def is_list_in_list(needle, haystack):
  163 + for n in needle:
  164 + if n not in haystack:
  165 + return False
  166 + return True
  167 +
  168 +
165 169 def datetime_from_list(time_list):
166 170 """
167 171 Datetimes in retrieved CDFs are stored as lists of numbers,
... ... @@ -276,9 +280,26 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at):
276 280 # The order matters. If you change this you also need to change the
277 281 # innermost loop of `get_data_for_target`.
278 282 # The javascript knows the targets' properties under these names.
  283 +# DEPRECATED, use PARAMETERS instead
279 284 PROPERTIES = ('time', 'vrad', 'vtan', 'vlen', 'magn', 'temp', 'pdyn', 'dens',
280 285 'angl', 'xhci', 'yhci')
281 286  
  287 +PARAMETERS = {
  288 + 'vlen': {
  289 + 'slug': 'vlen',
  290 + 'name': 'Velocity',
  291 + 'unit': 'km/s',
  292 + 'show': True,
  293 + },
  294 + 'magn': {
  295 + 'slug': 'magn',
  296 + 'name': 'Magnetism',
  297 + 'unit': 'nT',
  298 + 'show': True,
  299 + },
  300 + # fixme: add more
  301 +}
  302 +
282 303  
283 304 def get_data_for_target(target_config, started_at, stopped_at):
284 305 """
... ... @@ -509,12 +530,11 @@ def download_target_csv(target, started_at, stopped_at):
509 530 @app.route("/<targets>_<started_at>_<stopped_at>.tar.gz")
510 531 def download_targets_tarball(targets, started_at, stopped_at):
511 532 """
512   - Grab data and orbit data for the specified `target`,
513   - rearrange it and return it as a CSV file.
514   - `started_at` and `stopped_at` should be UTC.
  533 + Grab data and orbit data for each of the specified `targets`,
  534 + in their own CSV file, and make a tarball of them.
  535 + `started_at` and `stopped_at` should be UTC strings.
515 536  
516 537 targets: string list of targets' slugs, separated by `-`.
517   - This will fail hard if targets' slugs start having `-` in them.
518 538 """
519 539 separator = '-'
520 540 targets = targets.split(separator)
... ... @@ -566,6 +586,93 @@ def download_targets_tarball(targets, started_at, stopped_at):
566 586 return send_from_directory(get_path("../cache/"), gzip_filename)
567 587  
568 588  
  589 +@app.route("/<targets>_<params>_<started_at>_<stopped_at>.nc")
  590 +def download_targets_netcdf(targets, params, started_at, stopped_at):
  591 + """
  592 + Grab data and orbit data for the specified `target`,
  593 + rearrange it and return it as a CSV file.
  594 + `started_at` and `stopped_at` should be UTC.
  595 +
  596 + targets: string list of targets' slugs, separated by `-`.
  597 + params: string list of targets' parameters, separated by `-`.
  598 + """
  599 + separator = '-'
  600 + targets = targets.split(separator)
  601 + targets.sort()
  602 + targets_configs = []
  603 + for target in targets:
  604 + if not target:
  605 + abort(400, "Invalid targets format : `%s`." % targets)
  606 + targets_configs.append(get_target_config(target))
  607 + if 0 == len(targets_configs):
  608 + abort(400, "No valid targets specified. What are you doing?")
  609 + params = params.split(separator)
  610 + params.sort()
  611 + if 0 == len(params):
  612 + abort(400, "No valid parameters specified. What are you doing?")
  613 + if not is_list_in_list(params, PARAMETERS.keys()):
  614 + abort(400, "Some parameters are not recognized in '%s'." % str(params))
  615 +
  616 + date_fmt = "%Y-%m-%dT%H:%M:%S"
  617 + try:
  618 + started_at = datetime.datetime.strptime(started_at, date_fmt)
  619 + except:
  620 + abort(400, "Invalid started_at parameter : '%s'." % started_at)
  621 + try:
  622 + stopped_at = datetime.datetime.strptime(stopped_at, date_fmt)
  623 + except:
  624 + abort(400, "Invalid stopped_at parameter : '%s'." % stopped_at)
  625 + sta = started_at.strftime(date_fmt)
  626 + sto = stopped_at.strftime(date_fmt)
  627 +
  628 + nc_filename = "%s_%s_%s_%s.nc" % \
  629 + (separator.join(targets), separator.join(params), sta, sto)
  630 + nc_path = get_path("../cache/%s" % nc_filename)
  631 +
  632 + if not isfile(nc_path):
  633 + log.debug("Creating the NetCDF file '%s'..." % nc_filename)
  634 + nc_handle = Dataset(nc_path, "w", format="NETCDF4")
  635 + try:
  636 + nc_handle.description = "TODO" # todo
  637 + nc_handle.history = "Created " + time.ctime(time.time())
  638 + nc_handle.source = "Transplanet (CDDP)"
  639 + available_params = list(PROPERTIES)
  640 + for target in targets_configs:
  641 + target_slug = target['slug']
  642 + log.debug("Adding group '%s' to the NetCDF..." % target_slug)
  643 + nc_group = nc_handle.createGroup(target_slug)
  644 + data = get_data_for_target(target, started_at, stopped_at)
  645 + dkeys = sorted(data)
  646 + nc_handle.createDimension('dim_'+target_slug, len(dkeys))
  647 + # todo: add time
  648 + nc_vars = []
  649 + indices = []
  650 + for param in params:
  651 + indices.append(available_params.index(param))
  652 + nc_var = nc_group.createVariable(
  653 + param, 'f8', ('dim_'+target_slug,)
  654 + )
  655 + nc_var.units = PARAMETERS[param]['unit']
  656 + nc_vars.append(nc_var)
  657 + for i, nc_var in enumerate(nc_vars):
  658 + index = indices[i]
  659 + values = []
  660 + for dkey in dkeys:
  661 + dval = data[dkey]
  662 + values.append(dval[index])
  663 + nc_var[:] = values
  664 + # todo: add orbit x and y
  665 + except Exception as e:
  666 + raise e
  667 + finally:
  668 + nc_handle.close()
  669 +
  670 + if not isfile(nc_path):
  671 + abort(500, "No NetCDF to serve. Looked at '%s'." % nc_path)
  672 +
  673 + return send_from_directory(get_path("../cache/"), nc_filename)
  674 +
  675 +
569 676 # API #########################################################################
570 677  
571 678 @app.route("/cache/clear")
... ...