Commit bde97e4d3c1fc75b05aa98d61d6e87e4459504ce
1 parent
17c52617
Exists in
master
and in
2 other branches
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.
Showing
4 changed files
with
84 additions
and
61 deletions
Show diff stats
CHANGELOG.md
1 | 1 | ## Misc |
2 | 2 | |
3 | -- [ ] Support multiple models for each target | |
4 | 3 | - [ ] Optimize CSV generation (numpy vectorization ?) |
5 | 4 | - [ ] Credit the author of the pixel art planets |
6 | 5 | - [ ] Add a README to the download tarball |
... | ... | @@ -23,9 +22,13 @@ |
23 | 22 | - [ ] CRON statement to call it |
24 | 23 | - [ ] Cache warmup |
25 | 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 | 26 | - [x] Add a warning for users with javascript disabled |
28 | 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 | 34 | ## 0.0.0 | ... | ... |
web/run.py
1 | +# coding=utf-8 | |
2 | + | |
1 | 3 | import StringIO |
2 | 4 | import datetime |
3 | -import time | |
4 | 5 | import gzip |
5 | 6 | import json |
6 | 7 | import logging |
7 | 8 | import random |
8 | 9 | import tarfile |
10 | +import time | |
9 | 11 | import urllib |
10 | 12 | from csv import writer as csv_writer |
11 | 13 | from math import sqrt |
... | ... | @@ -15,7 +17,7 @@ from os.path import isfile, join, abspath, dirname |
15 | 17 | from flask import Flask |
16 | 18 | from flask import request |
17 | 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 | 21 | from netCDF4 import Dataset, date2num |
20 | 22 | from yaml import load as yaml_load |
21 | 23 | |
... | ... | @@ -101,6 +103,33 @@ def markdown_filter(value, nl2br=False, p=True): |
101 | 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 | 133 | tpl_engine = Environment(loader=FileSystemLoader([get_path('view')]), |
105 | 134 | trim_blocks=True, |
106 | 135 | lstrip_blocks=True) |
... | ... | @@ -113,6 +142,7 @@ tpl_engine.globals.update( |
113 | 142 | tpl_engine.filters['markdown'] = markdown_filter |
114 | 143 | tpl_engine.filters['md'] = markdown_filter |
115 | 144 | tpl_engine.filters['shuffle'] = shuffle_filter |
145 | +tpl_engine.filters['escapejs'] = escapejs_filter | |
116 | 146 | |
117 | 147 | tpl_global_vars = { |
118 | 148 | 'request': request, |
... | ... | @@ -278,11 +308,14 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at): |
278 | 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 | 314 | # The available parameters in the generated CSV files. |
282 | 315 | # The order matters. If you change this you also need to change the |
283 | 316 | # innermost loop of `get_data_for_target`. |
284 | 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 | 319 | 'angl', 'xhee', 'yhee') |
287 | 320 | |
288 | 321 | # The parameters that the users can handle. |
... | ... | @@ -293,36 +326,48 @@ PARAMETERS = { |
293 | 326 | 'name': 'Dyn. Pressure', |
294 | 327 | 'title': 'The dynamic pressure.', |
295 | 328 | 'units': 'nPa', |
329 | + 'active': True, | |
330 | + 'position': 10, | |
296 | 331 | }, |
297 | - 'vlen': { | |
298 | - 'slug': 'vlen', | |
332 | + 'vtot': { | |
333 | + 'slug': 'vtot', | |
299 | 334 | 'name': 'Velocity', |
300 | 335 | 'title': 'The velocity of the particles.', |
301 | 336 | 'units': 'km/s', |
337 | + 'active': False, | |
338 | + 'position': 20, | |
302 | 339 | }, |
303 | - 'magn': { | |
304 | - 'slug': 'magn', | |
340 | + 'btan': { | |
341 | + 'slug': 'btan', | |
305 | 342 | 'name': 'B Tangential', |
306 | 343 | 'title': 'B Tangential.', |
307 | 344 | 'units': 'nT', |
345 | + 'active': False, | |
346 | + 'position': 30, | |
308 | 347 | }, |
309 | 348 | 'temp': { |
310 | 349 | 'slug': 'temp', |
311 | 350 | 'name': 'Temperature', |
312 | 351 | 'title': 'The absolute temperature.', |
313 | 352 | 'units': 'K', |
353 | + 'active': False, | |
354 | + 'position': 40, | |
314 | 355 | }, |
315 | 356 | 'dens': { |
316 | 357 | 'slug': 'dens', |
317 | 358 | 'name': 'Density', |
318 | 359 | 'title': 'The density N.', |
319 | - 'units': 'cm^-3', | |
360 | + 'units': u'cm⁻³', | |
361 | + 'active': False, | |
362 | + 'position': 50, | |
320 | 363 | }, |
321 | 364 | 'angl': { |
322 | 365 | 'slug': 'angl', |
323 | 366 | 'name': 'Angle T-S-E', |
324 | 367 | 'title': 'Angle Target-Sun-Earth.', |
325 | 368 | 'units': 'deg', |
369 | + 'active': False, | |
370 | + 'position': 60, | |
326 | 371 | }, |
327 | 372 | } |
328 | 373 | |
... | ... | @@ -528,7 +573,7 @@ tpl_global_vars['visits'] = get_hit_counter() |
528 | 573 | # ROUTING ##################################################################### |
529 | 574 | |
530 | 575 | @app.route('/favicon.ico') |
531 | -def favicon(): | |
576 | +def favicon(): # we want it served from the root, not from static/ | |
532 | 577 | return send_from_directory( |
533 | 578 | join(app.root_path, 'static', 'img'), |
534 | 579 | 'favicon.ico', mimetype='image/vnd.microsoft.icon' |
... | ... | @@ -540,8 +585,11 @@ def favicon(): |
540 | 585 | @app.route("/index.html") |
541 | 586 | def home(): |
542 | 587 | increment_hit_counter() |
588 | + parameters = PARAMETERS.values() | |
589 | + parameters.sort(key=lambda x: x['position']) | |
543 | 590 | return render_view('home.html.jinja2', { |
544 | 591 | 'targets': config['targets'], |
592 | + 'parameters': parameters, | |
545 | 593 | 'planets': [s for s in config['targets'] if s['type'] == 'planet'], |
546 | 594 | 'probes': [s for s in config['targets'] if s['type'] == 'probe'], |
547 | 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 | 799 | if not isfile(nc_path): |
752 | 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 | 805 | # API ######################################################################### |
... | ... | @@ -773,12 +821,18 @@ def cache_warmup(): |
773 | 821 | Warms up the cache for the current day. |
774 | 822 | Linked to SpaceWeather#edit in swapp.ls |
775 | 823 | """ |
776 | - from dateutil.relativedelta import relativedelta | |
777 | 824 | # relativedelta(years=3) |
778 | 825 | # startted_at = datetime.datetime.now() - relativedelta(years=3) |
779 | 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 | 836 | # DEV TOOLS ################################################################### |
783 | 837 | |
784 | 838 | # @app.route("/inspect") | ... | ... |
web/static/js/swapp.ls
... | ... | @@ -337,7 +337,7 @@ export class TimeSeries |
337 | 337 | # Data in y-axis |
338 | 338 | |
339 | 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 | 341 | # title : string, more descriptive, shown on the left of the Y axis |
342 | 342 | # target : target object, like described in configuration |
343 | 343 | # data : list of {x: <datetime>, y: <float>} | ... | ... |
web/view/home.html.jinja2
... | ... | @@ -99,13 +99,9 @@ |
99 | 99 | <span class="mdl-layout-title">{{ icon('flask') }} Parameters</span> |
100 | 100 | |
101 | 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 | 105 | </nav> |
110 | 106 | |
111 | 107 | <div class="mdl-layout-spacer"></div> |
... | ... | @@ -537,7 +533,7 @@ |
537 | 533 | var configuration = { |
538 | 534 | time_series_container: '#time_series', |
539 | 535 | orbits_container: '#orbits', |
540 | - api : { | |
536 | + api: { | |
541 | 537 | 'data_for_interval': "{{ request.url_root }}<target>_<started_at>_<stopped_at>.csv", |
542 | 538 | 'download': "{{ request.url_root }}<targets>_<started_at>_<stopped_at>.tar.gz", |
543 | 539 | 'samp': "{{ request.url_root }}<targets>_<params>_<started_at>_<stopped_at>.nc" |
... | ... | @@ -545,7 +541,7 @@ var configuration = { |
545 | 541 | sun: { |
546 | 542 | img: '{{ static('img/sun_128.png') }}' |
547 | 543 | }, |
548 | - targets : { | |
544 | + targets: { | |
549 | 545 | {% for target in targets if not target.locked %} |
550 | 546 | {% if not loop.first %},{% endif %} |
551 | 547 | '{{ target.slug }}': { |
... | ... | @@ -557,45 +553,15 @@ var configuration = { |
557 | 553 | } |
558 | 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 | ... | ... |