Commit 6491a1f1006f76a7f044a2656f2bd426b49edaf2
1 parent
3faa136f
Exists in
master
and in
2 other branches
Fix up the bugs listed by Vincent.
Showing
7 changed files
with
108 additions
and
44 deletions
Show diff stats
CHANGELOG.md
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | - [ ] Cache cleanup |
22 | 22 | - [x] API at /cache/clear |
23 | 23 | - [ ] CRON statement to call it |
24 | +- [ ] Cache warmup | |
24 | 25 | - [x] Download raw data (tarball of CSV) for current time interval and targets |
25 | 26 | - [ ] Download raw data as NetCDF for current everything, via SAMP |
26 | 27 | - [x] Add a warning for users with javascript disabled | ... | ... |
config.yml
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | # The HTML metadata in the page header. |
6 | 6 | # Don't put double quotes (") in any of these fields. |
7 | 7 | meta: |
8 | - title: Space Weather | |
8 | + title: Heliopropa ⋅ Solar System Weather | |
9 | 9 | keywords: |
10 | 10 | - science |
11 | 11 | - cdpp |
... | ... | @@ -15,6 +15,16 @@ meta: |
15 | 15 | # Will be shown by search engines below the title of the page. |
16 | 16 | description: Space weather predictions around solar celestial bodies. |
17 | 17 | |
18 | +# The top bar | |
19 | +header: | |
20 | + title: CDPP / Heliopropa | |
21 | + links: | |
22 | + - text: Model | |
23 | + href: "https://onlinelibrary.wiley.com/doi/10.1029/2004JA010959/abstract" | |
24 | + - text: Horizon 2020 | |
25 | + href: "https://ec.europa.eu/programmes/horizon2020/" | |
26 | + | |
27 | + | |
18 | 28 | |
19 | 29 | # A list of authors that will appear in the HTML metadata and possibly in the |
20 | 30 | # authorship page as well. The order does not matter, it will be shuffled. | ... | ... |
web/run.py
... | ... | @@ -283,7 +283,7 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at): |
283 | 283 | # innermost loop of `get_data_for_target`. |
284 | 284 | # The javascript knows the targets' properties under these names. |
285 | 285 | PROPERTIES = ('time', 'vrad', 'vtan', 'vlen', 'magn', 'temp', 'pdyn', 'dens', |
286 | - 'angl', 'xhci', 'yhci') | |
286 | + 'angl', 'xhee', 'yhee') | |
287 | 287 | |
288 | 288 | # The parameters that the users can handle. |
289 | 289 | # The slug must be one of the properties above. |
... | ... | @@ -371,12 +371,12 @@ def get_data_for_target(target_config, started_at, stopped_at): |
371 | 371 | (target_config['name'], orbit_file)) |
372 | 372 | cdf_handle = Dataset(orbit_file, "r", format="NETCDF4") |
373 | 373 | times = cdf_handle.variables['Time'] # YYYY DOY HH MM SS .ms |
374 | - data_hci = cdf_handle.variables['HCI'] | |
375 | - for time, datum_hci in zip(times, data_hci): | |
374 | + data_hee = cdf_handle.variables['HEE'] | |
375 | + for time, datum_hee in zip(times, data_hee): | |
376 | 376 | dtime = datetime_from_list(time) |
377 | 377 | if started_at <= dtime <= stopped_at: |
378 | 378 | dkey = dtime.strftime(precision) |
379 | - orbit_data[dkey] = datum_hci | |
379 | + orbit_data[dkey] = datum_hee | |
380 | 380 | cdf_handle.close() |
381 | 381 | |
382 | 382 | all_data = {} # keys are datetime as str, values tuples of data |
... | ... | @@ -401,16 +401,16 @@ def get_data_for_target(target_config, started_at, stopped_at): |
401 | 401 | dtime = datetime_from_list(time) |
402 | 402 | if started_at <= dtime <= stopped_at: |
403 | 403 | dkey = dtime.strftime(precision) |
404 | - x_hci = None | |
405 | - y_hci = None | |
404 | + x_hee = None | |
405 | + y_hee = None | |
406 | 406 | if dkey in orbit_data: |
407 | - x_hci = orbit_data[dkey][0] | |
408 | - y_hci = orbit_data[dkey][1] | |
407 | + x_hee = orbit_data[dkey][0] | |
408 | + y_hee = orbit_data[dkey][1] | |
409 | 409 | all_data[dkey] = ( |
410 | 410 | dtime.strftime("%Y-%m-%dT%H:%M:%S+00:00"), |
411 | 411 | vrad, vtan, sqrt(vrad * vrad + vtan * vtan), |
412 | 412 | datum_b, datum_t, datum_n, datum_p, datum_d, |
413 | - x_hci, y_hci | |
413 | + x_hee, y_hee | |
414 | 414 | ) |
415 | 415 | cdf_handle.close() |
416 | 416 | |
... | ... | @@ -726,14 +726,14 @@ def download_targets_netcdf(targets, params, started_at, stopped_at): |
726 | 726 | nc_var[:] = values |
727 | 727 | |
728 | 728 | # ORBIT # |
729 | - nc_x = nc_group.createVariable('xhci', 'f8', (dimension,)) | |
729 | + nc_x = nc_group.createVariable('xhee', 'f8', (dimension,)) | |
730 | 730 | nc_x.units = 'Au' |
731 | - nc_y = nc_group.createVariable('yhci', 'f8', (dimension,)) | |
731 | + nc_y = nc_group.createVariable('yhee', 'f8', (dimension,)) | |
732 | 732 | nc_y.units = 'Au' |
733 | 733 | values_x = [] |
734 | 734 | values_y = [] |
735 | - index_x = available_params.index('xhci') | |
736 | - index_y = available_params.index('yhci') | |
735 | + index_x = available_params.index('xhee') | |
736 | + index_y = available_params.index('yhee') | |
737 | 737 | for dkey in dkeys: |
738 | 738 | dval = data[dkey] |
739 | 739 | values_x.append(dval[index_x]) | ... | ... |
web/static/js/swapp.js
... | ... | @@ -42,7 +42,7 @@ |
42 | 42 | SpaceWeather.prototype.init = function(){ |
43 | 43 | "This is called by the inline bootstrap javascript code.\nThis ain't in the constructor because it might return a Promise later on.\n(for the loader, for example)"; |
44 | 44 | var started_at, stopped_at, this$ = this; |
45 | - started_at = moment().subtract(1, 'year').hours(0).minutes(0).seconds(0); | |
45 | + started_at = moment().subtract(6, 'month').hours(0).minutes(0).seconds(0); | |
46 | 46 | stopped_at = moment().add(1, 'week').hours(0).minutes(0).seconds(0); |
47 | 47 | this.setStartAndStop(started_at, stopped_at); |
48 | 48 | this.loadAndCreatePlots(started_at, stopped_at); |
... | ... | @@ -126,7 +126,7 @@ |
126 | 126 | console.debug("Requested CSV for " + target_slug + "...", csv); |
127 | 127 | timeFormat = d3.timeParse('%Y-%m-%dT%H:%M:%S%Z'); |
128 | 128 | data = { |
129 | - 'hci': [] | |
129 | + 'hee': [] | |
130 | 130 | }; |
131 | 131 | configuration['parameters'].forEach(function(parameter){ |
132 | 132 | return data[parameter['id']] = []; |
... | ... | @@ -148,11 +148,11 @@ |
148 | 148 | y: parseFloat(d[id]) |
149 | 149 | }); |
150 | 150 | }); |
151 | - if (d['xhci'] && d['yhci']) { | |
152 | - return data['hci'].push({ | |
151 | + if (d['xhee'] && d['yhee']) { | |
152 | + return data['hee'].push({ | |
153 | 153 | t: dtime, |
154 | - x: parseFloat(d['xhci']), | |
155 | - y: parseFloat(d['yhci']) | |
154 | + x: parseFloat(d['xhee']), | |
155 | + y: parseFloat(d['yhee']) | |
156 | 156 | }); |
157 | 157 | } |
158 | 158 | }); |
... | ... | @@ -180,12 +180,11 @@ |
180 | 180 | console.info("Loading CSV data of " + target.name + "…"); |
181 | 181 | targetButton = $(".targets-filters .target." + target.slug); |
182 | 182 | targetButton.addClass('loading'); |
183 | - targetButton.removeClass('failed'); | |
184 | - targetButton.removeClass('empty'); | |
183 | + targetButton.removeClass('failed empty'); | |
185 | 184 | return this$.loadData(target.slug, started_at, stopped_at).then(function(data){ |
186 | 185 | console.info("Loaded CSV data of " + target.name + ".", data); |
187 | 186 | this$.createTimeSeries(target, data); |
188 | - this$.orbits.initOrbiter(target.slug, target.config, data['hci']); | |
187 | + this$.orbits.initOrbiter(target.slug, target.config, data['hee']); | |
189 | 188 | targetButton.removeClass('loading'); |
190 | 189 | if (target.active) { |
191 | 190 | return this$.hideLoader(); |
... | ... | @@ -392,7 +391,7 @@ |
392 | 391 | }); |
393 | 392 | }; |
394 | 393 | TimeSeries.prototype.init = function(){ |
395 | - var clipId, dx, this$ = this; | |
394 | + var formatMillisecond, formatSecond, formatMinute, formatHour, formatDay, formatWeek, formatMonth, formatYear, multiFormat, clipId, dx, this$ = this; | |
396 | 395 | console.info("Initializing plot of " + this + "…"); |
397 | 396 | this.margin = { |
398 | 397 | top: 30, |
... | ... | @@ -402,7 +401,40 @@ |
402 | 401 | }; |
403 | 402 | this.xScale = d3.scaleTime().domain(this.xDataExtent); |
404 | 403 | this.yScale = d3.scaleLinear().domain(this.yDataExtent); |
405 | - this.xAxis = d3.axisBottom().ticks(7); | |
404 | + formatMillisecond = d3.timeFormat(".%L"); | |
405 | + formatSecond = d3.timeFormat(":%S"); | |
406 | + formatMinute = d3.timeFormat("%I:%M"); | |
407 | + formatHour = d3.timeFormat("%I:%M"); | |
408 | + formatDay = d3.timeFormat("%a %d"); | |
409 | + formatWeek = d3.timeFormat("%b %d"); | |
410 | + formatMonth = d3.timeFormat("%B"); | |
411 | + formatYear = d3.timeFormat("%Y"); | |
412 | + multiFormat = function(date){ | |
413 | + if (date > d3.timeSecond(date)) { | |
414 | + return formatMillisecond(date); | |
415 | + } | |
416 | + if (date > d3.timeMinute(date)) { | |
417 | + return formatSecond(date); | |
418 | + } | |
419 | + if (date > d3.timeHour(date)) { | |
420 | + return formatMinute(date); | |
421 | + } | |
422 | + if (date > d3.timeDay(date)) { | |
423 | + return formatHour(date); | |
424 | + } | |
425 | + if (date > d3.timeMonth(date)) { | |
426 | + if (date > d3.timeWeek(date)) { | |
427 | + return formatDay(date); | |
428 | + } else { | |
429 | + return formatWeek(date); | |
430 | + } | |
431 | + } | |
432 | + if (date > d3.timeYear(date)) { | |
433 | + return formatMonth(date); | |
434 | + } | |
435 | + return formatYear(date); | |
436 | + }; | |
437 | + this.xAxis = d3.axisBottom().tickFormat(multiFormat).ticks(7); | |
406 | 438 | this.yAxis = d3.axisLeft().ticks(10); |
407 | 439 | this.line = d3.line().x(function(d){ |
408 | 440 | return this$.xScale(d.x); |
... | ... | @@ -563,7 +595,7 @@ |
563 | 595 | TimeSeries.prototype.bisectDate = d3.bisector(function(d){ |
564 | 596 | return d.x; |
565 | 597 | }).left; |
566 | - TimeSeries.prototype.timeFormat = d3.timeFormat("%Y-%m-%d %Hh"); | |
598 | + TimeSeries.prototype.timeFormat = d3.timeFormat("%Y-%m-%d %H:%M"); | |
567 | 599 | TimeSeries.prototype.moveCursor = function(x0){ |
568 | 600 | var i, d0, d1, d, xx, yy, mirrored, dx, transform; |
569 | 601 | i = this.bisectDate(this.data, x0, 1); |
... | ... | @@ -634,7 +666,7 @@ |
634 | 666 | this.yAxisTitle.append('tspan').attr('dy', '-3px').text(' (AU)'); |
635 | 667 | this.yAxisTitle.attr('transform', 'rotate(-90)'); |
636 | 668 | this.sun = this.plotWrapper.append("svg:image").attr('xlink:href', this.options.sun.img).attr('width', '32px').attr('height', '32px'); |
637 | - this.sun.append('svg:title').text("Sol"); | |
669 | + this.sun.append('svg:title').text("Sun"); | |
638 | 670 | $(this.svg.node()).hide(); |
639 | 671 | return this.resize(); |
640 | 672 | }; |
... | ... | @@ -651,6 +683,7 @@ |
651 | 683 | this.yScale = d3.scaleLinear().domain([-1 * this.extremum, this.extremum]); |
652 | 684 | orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true); |
653 | 685 | orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px'); |
686 | + orbiter.append('svg:title').text(config.name); | |
654 | 687 | orbit_line = d3.line().x(function(d){ |
655 | 688 | return this$.xScale(d.x); |
656 | 689 | }).y(function(d){ | ... | ... |
web/static/js/swapp.ls
... | ... | @@ -74,7 +74,7 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE |
74 | 74 | """ |
75 | 75 | # Default time interval is from two weeks ago to one week ahead. |
76 | 76 | # We set the h/m/s to zero to benefit from a daily cache. |
77 | - started_at = moment().subtract(1, 'year').hours(0).minutes(0).seconds(0) | |
77 | + started_at = moment().subtract(6, 'month').hours(0).minutes(0).seconds(0) | |
78 | 78 | stopped_at = moment().add(1, 'week').hours(0).minutes(0).seconds(0) |
79 | 79 | @setStartAndStop(started_at, stopped_at) |
80 | 80 | @loadAndCreatePlots(started_at, stopped_at) |
... | ... | @@ -138,7 +138,7 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE |
138 | 138 | d3.csv(url, (csv) -> |
139 | 139 | console.debug("Requested CSV for #{target_slug}...", csv) |
140 | 140 | timeFormat = d3.timeParse('%Y-%m-%dT%H:%M:%S%Z') |
141 | - data = { 'hci': [] } | |
141 | + data = { 'hee': [] } | |
142 | 142 | configuration['parameters'].forEach((parameter) -> |
143 | 143 | data[parameter['id']] = [] |
144 | 144 | ) |
... | ... | @@ -150,9 +150,9 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE |
150 | 150 | id = parameter['id'] |
151 | 151 | data[id].push({x: dtime, y: parseFloat(d[id])}) |
152 | 152 | ) |
153 | - if d['xhci'] and d['yhci'] | |
154 | - data['hci'].push({ | |
155 | - t: dtime, x: parseFloat(d['xhci']), y: parseFloat(d['yhci']) | |
153 | + if d['xhee'] and d['yhee'] | |
154 | + data['hee'].push({ | |
155 | + t: dtime, x: parseFloat(d['xhee']), y: parseFloat(d['yhee']) | |
156 | 156 | }) |
157 | 157 | ) |
158 | 158 | resolve data |
... | ... | @@ -175,13 +175,12 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE |
175 | 175 | console.info "Loading CSV data of #{target.name}…" |
176 | 176 | targetButton = $(".targets-filters .target.#{target.slug}") |
177 | 177 | targetButton.addClass('loading') |
178 | - targetButton.removeClass('failed') | |
179 | - targetButton.removeClass('empty') # fixme | |
178 | + targetButton.removeClass('failed empty') | |
180 | 179 | @loadData(target.slug, started_at, stopped_at).then( |
181 | 180 | (data) ~> |
182 | 181 | console.info "Loaded CSV data of #{target.name}.", data |
183 | 182 | @createTimeSeries(target, data) |
184 | - @orbits.initOrbiter(target.slug, target.config, data['hci']) | |
183 | + @orbits.initOrbiter(target.slug, target.config, data['hee']) | |
185 | 184 | targetButton.removeClass('loading') |
186 | 185 | if target.active then @hideLoader() else @disableTarget(target.slug) |
187 | 186 | , |
... | ... | @@ -341,8 +340,28 @@ export class TimeSeries |
341 | 340 | @xScale = d3.scaleTime().domain(@xDataExtent) |
342 | 341 | @yScale = d3.scaleLinear().domain(@yDataExtent) |
343 | 342 | |
343 | + formatMillisecond = d3.timeFormat(".%L") | |
344 | + formatSecond = d3.timeFormat(":%S") | |
345 | + formatMinute = d3.timeFormat("%I:%M") | |
346 | + formatHour = d3.timeFormat("%I:%M") | |
347 | + formatDay = d3.timeFormat("%a %d") | |
348 | + formatWeek = d3.timeFormat("%b %d") | |
349 | + formatMonth = d3.timeFormat("%B") | |
350 | + formatYear = d3.timeFormat("%Y") | |
351 | + | |
352 | + multiFormat = (date) -> | |
353 | + if date > d3.timeSecond(date) then return formatMillisecond(date) | |
354 | + if date > d3.timeMinute(date) then return formatSecond(date) | |
355 | + if date > d3.timeHour(date) then return formatMinute(date) | |
356 | + if date > d3.timeDay(date) then return formatHour(date) | |
357 | + if date > d3.timeMonth(date) | |
358 | + if date > d3.timeWeek(date) then return formatDay(date) | |
359 | + else return formatWeek(date) | |
360 | + if date > d3.timeYear(date) then return formatMonth(date) | |
361 | + return formatYear(date) | |
362 | + | |
344 | 363 | @xAxis = d3.axisBottom() |
345 | -# .tickFormat(d3.timeFormat("%Y-%m-%d")) | |
364 | + .tickFormat(multiFormat) | |
346 | 365 | .ticks(7) |
347 | 366 | @yAxis = d3.axisLeft() |
348 | 367 | .ticks(10) |
... | ... | @@ -554,7 +573,7 @@ export class TimeSeries |
554 | 573 | @focus.style("display", "none") |
555 | 574 | |
556 | 575 | bisectDate: d3.bisector((d) -> d.x).left # /!\ complex |
557 | - timeFormat: d3.timeFormat("%Y-%m-%d %Hh") | |
576 | + timeFormat: d3.timeFormat("%Y-%m-%d %H:%M") | |
558 | 577 | |
559 | 578 | moveCursor: (x0) -> |
560 | 579 | i = @bisectDate(@data, x0, 1) |
... | ... | @@ -610,7 +629,7 @@ export class Orbits |
610 | 629 | left: 60 |
611 | 630 | } |
612 | 631 | |
613 | - @data = {} # slug => HCI array | |
632 | + @data = {} # slug => HEE array | |
614 | 633 | @orbiters = {} # slug => config |
615 | 634 | @orbitersElements = {} |
616 | 635 | @extremum = 1 |
... | ... | @@ -647,7 +666,7 @@ export class Orbits |
647 | 666 | @sun = @plotWrapper.append("svg:image") |
648 | 667 | .attr('xlink:href', @options.sun.img) |
649 | 668 | .attr('width', '32px').attr('height', '32px') |
650 | - @sun.append('svg:title').text("Sol") | |
669 | + @sun.append('svg:title').text("Sun") | |
651 | 670 | |
652 | 671 | $(@svg.node()).hide(); # we'll show it later when there'll be data |
653 | 672 | @resize() |
... | ... | @@ -668,6 +687,7 @@ export class Orbits |
668 | 687 | orbiter = @plotWrapper.append("svg:image") |
669 | 688 | .attr('xlink:href', config['img']) |
670 | 689 | .attr('width', '32px').attr('height', '32px') |
690 | + orbiter.append('svg:title').text(config.name) | |
671 | 691 | |
672 | 692 | orbit_line = d3.line() |
673 | 693 | .x((d) ~> @xScale(d.x)) | ... | ... |
web/view/home.html.jinja2
... | ... | @@ -325,19 +325,19 @@ |
325 | 325 | cursor: not-allowed; |
326 | 326 | } |
327 | 327 | .targets-filters .target.loading { |
328 | - -webkit-animation-name: spin; | |
328 | + -webkit-animation-name: keyframes_rotate; | |
329 | 329 | -webkit-animation-duration: 4000ms; |
330 | 330 | -webkit-animation-iteration-count: infinite; |
331 | 331 | -webkit-animation-timing-function: linear; |
332 | - -moz-animation-name: spin; | |
332 | + -moz-animation-name: keyframes_rotate; | |
333 | 333 | -moz-animation-duration: 4000ms; |
334 | 334 | -moz-animation-iteration-count: infinite; |
335 | 335 | -moz-animation-timing-function: linear; |
336 | - -ms-animation-name: spin; | |
336 | + -ms-animation-name: keyframes_rotate; | |
337 | 337 | -ms-animation-duration: 4000ms; |
338 | 338 | -ms-animation-iteration-count: infinite; |
339 | 339 | -ms-animation-timing-function: linear; |
340 | - animation-name: spin; | |
340 | + animation-name: keyframes_rotate; | |
341 | 341 | animation-duration: 4000ms; |
342 | 342 | animation-iteration-count: infinite; |
343 | 343 | animation-timing-function: linear; | ... | ... |
web/view/layout.html.jinja2
... | ... | @@ -42,7 +42,7 @@ |
42 | 42 | <!-- Navigation. We hide it in small screens. --> |
43 | 43 | <nav class="mdl-navigation mdl-layout--large-screen-only"> |
44 | 44 | <a class="mdl-navigation__link" href="https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE">v{{ version }}</a> |
45 | - <a class="mdl-navigation__link" href="#">Model</a> | |
45 | + <a class="mdl-navigation__link" href="https://onlinelibrary.wiley.com/doi/10.1029/2004JA010959/abstract">Model</a> | |
46 | 46 | <a class="mdl-navigation__link" href="https://ec.europa.eu/programmes/horizon2020/">Horizon 2020</a> |
47 | 47 | </nav> |
48 | 48 | </div> | ... | ... |