Commit bde97e4d3c1fc75b05aa98d61d6e87e4459504ce

Authored by Goutte
1 parent 17c52617

Add more changes suggested by Vincent. Still hunting why the recent data is not …

…shown, but from as far as I can see, it's not in the CDF files provided by AMDA.
1 ## Misc 1 ## Misc
2 2
3 -- [ ] Support multiple models for each target  
4 - [ ] Optimize CSV generation (numpy vectorization ?) 3 - [ ] Optimize CSV generation (numpy vectorization ?)
5 - [ ] Credit the author of the pixel art planets 4 - [ ] Credit the author of the pixel art planets
6 - [ ] Add a README to the download tarball 5 - [ ] Add a README to the download tarball
@@ -23,9 +22,13 @@ @@ -23,9 +22,13 @@
23 - [ ] CRON statement to call it 22 - [ ] CRON statement to call it
24 - [ ] Cache warmup 23 - [ ] Cache warmup
25 - [x] Download raw data (tarball of CSV) for current time interval and targets 24 - [x] Download raw data (tarball of CSV) for current time interval and targets
26 -- [ ] Download raw data as NetCDF for current everything, via SAMP 25 +- [x] Download raw data as NetCDF for current everything, via SAMP
27 - [x] Add a warning for users with javascript disabled 26 - [x] Add a warning for users with javascript disabled
28 - [ ] Set the log level to _error_ in production (see `web/run.py`) 27 - [ ] Set the log level to _error_ in production (see `web/run.py`)
  28 +- [ ] Make the targets dynamic in the orbit plot, allowing zoom
  29 +- [ ] Add units to the generated CSV files
  30 +- [x] Support multiple models for each target
  31 +
29 32
30 33
31 ## 0.0.0 34 ## 0.0.0
  1 +# coding=utf-8
  2 +
1 import StringIO 3 import StringIO
2 import datetime 4 import datetime
3 -import time  
4 import gzip 5 import gzip
5 import json 6 import json
6 import logging 7 import logging
7 import random 8 import random
8 import tarfile 9 import tarfile
  10 +import time
9 import urllib 11 import urllib
10 from csv import writer as csv_writer 12 from csv import writer as csv_writer
11 from math import sqrt 13 from math import sqrt
@@ -15,7 +17,7 @@ from os.path import isfile, join, abspath, dirname @@ -15,7 +17,7 @@ from os.path import isfile, join, abspath, dirname
15 from flask import Flask 17 from flask import Flask
16 from flask import request 18 from flask import request
17 from flask import url_for, send_from_directory, abort as abort_flask 19 from flask import url_for, send_from_directory, abort as abort_flask
18 -from jinja2 import Environment, FileSystemLoader 20 +from jinja2 import Environment, FileSystemLoader, Markup
19 from netCDF4 import Dataset, date2num 21 from netCDF4 import Dataset, date2num
20 from yaml import load as yaml_load 22 from yaml import load as yaml_load
21 23
@@ -101,6 +103,33 @@ def markdown_filter(value, nl2br=False, p=True): @@ -101,6 +103,33 @@ def markdown_filter(value, nl2br=False, p=True):
101 return markdowned 103 return markdowned
102 104
103 105
  106 +_js_escapes = {
  107 + '\\': '\\u005C',
  108 + '\'': '\\u0027',
  109 + '"': '\\u0022',
  110 + '>': '\\u003E',
  111 + '<': '\\u003C',
  112 + '&': '\\u0026',
  113 + '=': '\\u003D',
  114 + '-': '\\u002D',
  115 + ';': '\\u003B',
  116 + u'\u2028': '\\u2028',
  117 + u'\u2029': '\\u2029'
  118 +}
  119 +# Escape every ASCII character with a value less than 32.
  120 +_js_escapes.update(('%c' % z, '\\u%04X' % z) for z in xrange(32))
  121 +
  122 +
  123 +def escapejs_filter(value):
  124 + escaped = []
  125 + for letter in value:
  126 + if letter in _js_escapes:
  127 + escaped.append(_js_escapes[letter])
  128 + else:
  129 + escaped.append(letter)
  130 +
  131 + return Markup("".join(escaped))
  132 +
104 tpl_engine = Environment(loader=FileSystemLoader([get_path('view')]), 133 tpl_engine = Environment(loader=FileSystemLoader([get_path('view')]),
105 trim_blocks=True, 134 trim_blocks=True,
106 lstrip_blocks=True) 135 lstrip_blocks=True)
@@ -113,6 +142,7 @@ tpl_engine.globals.update( @@ -113,6 +142,7 @@ tpl_engine.globals.update(
113 tpl_engine.filters['markdown'] = markdown_filter 142 tpl_engine.filters['markdown'] = markdown_filter
114 tpl_engine.filters['md'] = markdown_filter 143 tpl_engine.filters['md'] = markdown_filter
115 tpl_engine.filters['shuffle'] = shuffle_filter 144 tpl_engine.filters['shuffle'] = shuffle_filter
  145 +tpl_engine.filters['escapejs'] = escapejs_filter
116 146
117 tpl_global_vars = { 147 tpl_global_vars = {
118 'request': request, 148 'request': request,
@@ -278,11 +308,14 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at): @@ -278,11 +308,14 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at):
278 return local_netc_files 308 return local_netc_files
279 309
280 310
  311 +# These two configs are not in the YAML config because adding a new parameter
  312 +# will not work as-is, you'll have to edit the netcdf-related code
  313 +
281 # The available parameters in the generated CSV files. 314 # The available parameters in the generated CSV files.
282 # The order matters. If you change this you also need to change the 315 # The order matters. If you change this you also need to change the
283 # innermost loop of `get_data_for_target`. 316 # innermost loop of `get_data_for_target`.
284 # The javascript knows the targets' properties under these names. 317 # The javascript knows the targets' properties under these names.
285 -PROPERTIES = ('time', 'vrad', 'vtan', 'vlen', 'magn', 'temp', 'pdyn', 'dens', 318 +PROPERTIES = ('time', 'vrad', 'vtan', 'vtot', 'btan', 'temp', 'pdyn', 'dens',
286 'angl', 'xhee', 'yhee') 319 'angl', 'xhee', 'yhee')
287 320
288 # The parameters that the users can handle. 321 # The parameters that the users can handle.
@@ -293,36 +326,48 @@ PARAMETERS = { @@ -293,36 +326,48 @@ PARAMETERS = {
293 'name': 'Dyn. Pressure', 326 'name': 'Dyn. Pressure',
294 'title': 'The dynamic pressure.', 327 'title': 'The dynamic pressure.',
295 'units': 'nPa', 328 'units': 'nPa',
  329 + 'active': True,
  330 + 'position': 10,
296 }, 331 },
297 - 'vlen': {  
298 - 'slug': 'vlen', 332 + 'vtot': {
  333 + 'slug': 'vtot',
299 'name': 'Velocity', 334 'name': 'Velocity',
300 'title': 'The velocity of the particles.', 335 'title': 'The velocity of the particles.',
301 'units': 'km/s', 336 'units': 'km/s',
  337 + 'active': False,
  338 + 'position': 20,
302 }, 339 },
303 - 'magn': {  
304 - 'slug': 'magn', 340 + 'btan': {
  341 + 'slug': 'btan',
305 'name': 'B Tangential', 342 'name': 'B Tangential',
306 'title': 'B Tangential.', 343 'title': 'B Tangential.',
307 'units': 'nT', 344 'units': 'nT',
  345 + 'active': False,
  346 + 'position': 30,
308 }, 347 },
309 'temp': { 348 'temp': {
310 'slug': 'temp', 349 'slug': 'temp',
311 'name': 'Temperature', 350 'name': 'Temperature',
312 'title': 'The absolute temperature.', 351 'title': 'The absolute temperature.',
313 'units': 'K', 352 'units': 'K',
  353 + 'active': False,
  354 + 'position': 40,
314 }, 355 },
315 'dens': { 356 'dens': {
316 'slug': 'dens', 357 'slug': 'dens',
317 'name': 'Density', 358 'name': 'Density',
318 'title': 'The density N.', 359 'title': 'The density N.',
319 - 'units': 'cm^-3', 360 + 'units': u'cm⁻³',
  361 + 'active': False,
  362 + 'position': 50,
320 }, 363 },
321 'angl': { 364 'angl': {
322 'slug': 'angl', 365 'slug': 'angl',
323 'name': 'Angle T-S-E', 366 'name': 'Angle T-S-E',
324 'title': 'Angle Target-Sun-Earth.', 367 'title': 'Angle Target-Sun-Earth.',
325 'units': 'deg', 368 'units': 'deg',
  369 + 'active': False,
  370 + 'position': 60,
326 }, 371 },
327 } 372 }
328 373
@@ -528,7 +573,7 @@ tpl_global_vars[&#39;visits&#39;] = get_hit_counter() @@ -528,7 +573,7 @@ tpl_global_vars[&#39;visits&#39;] = get_hit_counter()
528 # ROUTING ##################################################################### 573 # ROUTING #####################################################################
529 574
530 @app.route('/favicon.ico') 575 @app.route('/favicon.ico')
531 -def favicon(): 576 +def favicon(): # we want it served from the root, not from static/
532 return send_from_directory( 577 return send_from_directory(
533 join(app.root_path, 'static', 'img'), 578 join(app.root_path, 'static', 'img'),
534 'favicon.ico', mimetype='image/vnd.microsoft.icon' 579 'favicon.ico', mimetype='image/vnd.microsoft.icon'
@@ -540,8 +585,11 @@ def favicon(): @@ -540,8 +585,11 @@ def favicon():
540 @app.route("/index.html") 585 @app.route("/index.html")
541 def home(): 586 def home():
542 increment_hit_counter() 587 increment_hit_counter()
  588 + parameters = PARAMETERS.values()
  589 + parameters.sort(key=lambda x: x['position'])
543 return render_view('home.html.jinja2', { 590 return render_view('home.html.jinja2', {
544 'targets': config['targets'], 591 'targets': config['targets'],
  592 + 'parameters': parameters,
545 'planets': [s for s in config['targets'] if s['type'] == 'planet'], 593 'planets': [s for s in config['targets'] if s['type'] == 'planet'],
546 'probes': [s for s in config['targets'] if s['type'] == 'probe'], 594 'probes': [s for s in config['targets'] if s['type'] == 'probe'],
547 'comets': [s for s in config['targets'] if s['type'] == 'comet'], 595 'comets': [s for s in config['targets'] if s['type'] == 'comet'],
@@ -751,7 +799,7 @@ def download_targets_netcdf(targets, params, started_at, stopped_at): @@ -751,7 +799,7 @@ def download_targets_netcdf(targets, params, started_at, stopped_at):
751 if not isfile(nc_path): 799 if not isfile(nc_path):
752 abort(500, "No NetCDF to serve. Looked at '%s'." % nc_path) 800 abort(500, "No NetCDF to serve. Looked at '%s'." % nc_path)
753 801
754 - return send_from_directory(get_path("../cache/"), nc_filename) 802 + return send_from_directory(get_path("../cache"), nc_filename)
755 803
756 804
757 # API ######################################################################### 805 # API #########################################################################
@@ -773,12 +821,18 @@ def cache_warmup(): @@ -773,12 +821,18 @@ def cache_warmup():
773 Warms up the cache for the current day. 821 Warms up the cache for the current day.
774 Linked to SpaceWeather#edit in swapp.ls 822 Linked to SpaceWeather#edit in swapp.ls
775 """ 823 """
776 - from dateutil.relativedelta import relativedelta  
777 # relativedelta(years=3) 824 # relativedelta(years=3)
778 # startted_at = datetime.datetime.now() - relativedelta(years=3) 825 # startted_at = datetime.datetime.now() - relativedelta(years=3)
779 return "To Do" 826 return "To Do"
780 827
781 828
  829 +@app.route("/run.log")
  830 +def run_log():
  831 + with open(get_path('run.log'), 'r') as f:
  832 + contents = f.read()
  833 + return contents
  834 +
  835 +
782 # DEV TOOLS ################################################################### 836 # DEV TOOLS ###################################################################
783 837
784 # @app.route("/inspect") 838 # @app.route("/inspect")
web/static/js/swapp.ls
@@ -337,7 +337,7 @@ export class TimeSeries @@ -337,7 +337,7 @@ export class TimeSeries
337 # Data in y-axis 337 # Data in y-axis
338 338
339 (@parameter, @title, @target, data, @visible, @container, @options = {}) -> 339 (@parameter, @title, @target, data, @visible, @container, @options = {}) ->
340 - # parameter : slug of the parameter to observe, like magn or pdyn 340 + # parameter : slug of the parameter to observe, like btan or pdyn
341 # title : string, more descriptive, shown on the left of the Y axis 341 # title : string, more descriptive, shown on the left of the Y axis
342 # target : target object, like described in configuration 342 # target : target object, like described in configuration
343 # data : list of {x: <datetime>, y: <float>} 343 # data : list of {x: <datetime>, y: <float>}
web/view/home.html.jinja2
@@ -99,13 +99,9 @@ @@ -99,13 +99,9 @@
99 <span class="mdl-layout-title">{{ icon('flask') }} Parameters</span> 99 <span class="mdl-layout-title">{{ icon('flask') }} Parameters</span>
100 100
101 <nav id="parameters" class="mdl-navigation"> 101 <nav id="parameters" class="mdl-navigation">
102 - {# todo: generate this from config #}  
103 - <a class="mdl-navigation__link parameter active" data-ts-slug="pdyn" href="#">Dynamic Pressure</a>  
104 - <a class="mdl-navigation__link parameter" data-ts-slug="magn" href="#">B Tangential</a>  
105 - <a class="mdl-navigation__link parameter" data-ts-slug="vlen" href="#">Velocity</a>  
106 - <a class="mdl-navigation__link parameter" data-ts-slug="temp" href="#">Temperature</a>  
107 - <a class="mdl-navigation__link parameter" data-ts-slug="dens" href="#">Density</a>  
108 - <a class="mdl-navigation__link parameter" data-ts-slug="angl" href="#">Angle Target-Sun-Earth</a> 102 + {% for p in parameters %}
  103 + <a class="mdl-navigation__link parameter{{ ' active' if p.active }}" data-ts-slug="{{ p.slug }}" href="#">{{ p.name }}</a>
  104 + {% endfor %}
109 </nav> 105 </nav>
110 106
111 <div class="mdl-layout-spacer"></div> 107 <div class="mdl-layout-spacer"></div>
@@ -537,7 +533,7 @@ @@ -537,7 +533,7 @@
537 var configuration = { 533 var configuration = {
538 time_series_container: '#time_series', 534 time_series_container: '#time_series',
539 orbits_container: '#orbits', 535 orbits_container: '#orbits',
540 - api : { 536 + api: {
541 'data_for_interval': "{{ request.url_root }}<target>_<started_at>_<stopped_at>.csv", 537 'data_for_interval': "{{ request.url_root }}<target>_<started_at>_<stopped_at>.csv",
542 'download': "{{ request.url_root }}<targets>_<started_at>_<stopped_at>.tar.gz", 538 'download': "{{ request.url_root }}<targets>_<started_at>_<stopped_at>.tar.gz",
543 'samp': "{{ request.url_root }}<targets>_<params>_<started_at>_<stopped_at>.nc" 539 'samp': "{{ request.url_root }}<targets>_<params>_<started_at>_<stopped_at>.nc"
@@ -545,7 +541,7 @@ var configuration = { @@ -545,7 +541,7 @@ var configuration = {
545 sun: { 541 sun: {
546 img: '{{ static('img/sun_128.png') }}' 542 img: '{{ static('img/sun_128.png') }}'
547 }, 543 },
548 - targets : { 544 + targets: {
549 {% for target in targets if not target.locked %} 545 {% for target in targets if not target.locked %}
550 {% if not loop.first %},{% endif %} 546 {% if not loop.first %},{% endif %}
551 '{{ target.slug }}': { 547 '{{ target.slug }}': {
@@ -557,45 +553,15 @@ var configuration = { @@ -557,45 +553,15 @@ var configuration = {
557 } 553 }
558 {% endfor %} 554 {% endfor %}
559 }, 555 },
560 -{# todo @Nicolas Define -somehow- error margins of each parameter ? #}  
561 -{# todo: generate this from config PARAMETERS #}  
562 - parameters : [ 556 + parameters: [
  557 +{% for p in parameters %}
563 { 558 {
564 - id: 'pdyn',  
565 - title: "Dyn. Pressure (nPa)",  
566 - active: true,  
567 - unit: "nPa"  
568 - },  
569 - {  
570 - id: 'magn',  
571 - title: "B Tangential (nT)",  
572 - active: false,  
573 - unit: "nT"  
574 - },  
575 - {  
576 - id: 'vlen',  
577 - title: "Velocity (km/s)",  
578 - active: false,  
579 - unit: "km/s"  
580 - },  
581 - {  
582 - id: 'temp',  
583 - title: "Temperature (K)",  
584 - active: false,  
585 - unit: "K"  
586 - },  
587 - {  
588 - id: 'dens',  
589 - title: "Density N (cm⁻³)",  
590 - active: false,  
591 - unit: "cm⁻³"  
592 - },  
593 - {  
594 - id: 'angl',  
595 - title: "Angle T-S-E (deg)",  
596 - active: false,  
597 - unit: "deg"  
598 - } 559 + id: "{{ p.slug | escapejs }}",
  560 + title: "{{ p.name | escapejs }} ({{ p.units | escapejs }})",
  561 + active: {{ 'true' if p.active else 'false' }},
  562 + unit: "{{ p.units | escapejs }}"
  563 + }{{ ',' if not loop.last }}
  564 +{% endfor %}
599 ] 565 ]
600 }; 566 };
601 567