Commit dc0be9925d35db1001545592b3b2bebe31a70421
1 parent
ff3519a8
Exists in
master
and in
2 other branches
Support having no position to display (for Rosetta in some intervals)
Make the local cache more resilient to corrupted downloads
Showing
7 changed files
with
69 additions
and
25 deletions
Show diff stats
CHANGELOG.md
... | ... | @@ -12,8 +12,8 @@ |
12 | 12 | ## 1.0.0-rc4 |
13 | 13 | |
14 | 14 | - [ ] Make the tarball with netcdf files instead of CSVs |
15 | -- [ ] Support having no position to display (for Rosetta in some intervals) | |
16 | -- [x] Cache clear (remove all files) `/cache/clear` | |
15 | +- [x] Support having no position to display (for Rosetta in some intervals) | |
16 | +- [x] Make the local cache more resilient to corrupted downloads | |
17 | 17 | - [x] Make the local cache more resilient to naming collisions |
18 | 18 | |
19 | 19 | |
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | |
22 | 22 | - [x] Make the targets dynamic in the orbit plot, allowing zoom |
23 | 23 | - [x] Refactor some more to move as much as we can to the config |
24 | +- [x] Cache clear (remove all files) `/cache/clear` | |
24 | 25 | |
25 | 26 | |
26 | 27 | ## 1.0.0-rc2 |
... | ... | @@ -65,7 +66,7 @@ |
65 | 66 | - [x] Probes |
66 | 67 | - [x] Rosetta |
67 | 68 | - [x] Juno |
68 | -- [x] Comets (the one visited by rosetta) | |
69 | +- [x] Comets (the one visited by rosetta, p67) | |
69 | 70 | - [x] Orbits axes titles |
70 | 71 | - [x] Logo Europlanet |
71 | 72 | - [x] Cache remote NetCDFs locally | ... | ... |
config.yml
... | ... | @@ -98,7 +98,7 @@ targets: |
98 | 98 | - type: 'planet' |
99 | 99 | slug: 'earth' |
100 | 100 | name: 'Earth' |
101 | - title: 'Earth (Coming NEXT!)' | |
101 | + title: 'Earth (coming soon)' | |
102 | 102 | orbit: |
103 | 103 | models: |
104 | 104 | - slug: 'earth_orb_all' |
... | ... | @@ -152,13 +152,13 @@ targets: |
152 | 152 | - type: 'probe' |
153 | 153 | slug: 'rosetta' |
154 | 154 | name: 'Rosetta' |
155 | - title: 'Rosetta (coming soon)' | |
155 | + title: 'Rosetta' | |
156 | 156 | orbit: |
157 | 157 | models: |
158 | 158 | - slug: 'ros_orb_cruise' |
159 | 159 | models: |
160 | 160 | - slug: 'tao_ros_sw' |
161 | - locked: true | |
161 | + locked: false | |
162 | 162 | default: false |
163 | 163 | - type: 'probe' |
164 | 164 | slug: 'juno' |
... | ... | @@ -169,7 +169,7 @@ targets: |
169 | 169 | - slug: 'juno_cruise_all' |
170 | 170 | models: |
171 | 171 | - slug: 'tao_juno_sw' |
172 | - locked: true | |
172 | + locked: false | |
173 | 173 | default: false |
174 | 174 | - type: 'comet' |
175 | 175 | slug: 'p67' | ... | ... |
web/run.py
... | ... | @@ -368,11 +368,12 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at): |
368 | 368 | log.debug("Retrieving '%s'..." % local_gzip_file) |
369 | 369 | urllib.urlretrieve(remote_gzip_file, local_gzip_file) |
370 | 370 | log.debug("Retrieved '%s'." % local_gzip_file) |
371 | + else: | |
372 | + log.debug("Found '%s' in the cache." % local_gzip_file) | |
371 | 373 | |
372 | 374 | local_netc_files = [] |
373 | 375 | for local_gzip_file in local_gzip_files: |
374 | 376 | local_netc_file = local_gzip_file[0:-3] |
375 | - local_netc_files.append(local_netc_file) | |
376 | 377 | log.debug("Unzipping '%s'..." % local_gzip_file) |
377 | 378 | success = True |
378 | 379 | try: |
... | ... | @@ -382,9 +383,13 @@ def retrieve_amda_netcdf(orbiter, what, started_at, stopped_at): |
382 | 383 | g.write(file_content) |
383 | 384 | except Exception as e: |
384 | 385 | success = False |
385 | - log.warning("Cannot process gz file '%s' from '%s' : %s" % | |
386 | - (local_gzip_file, url, e)) | |
386 | + log.error("Cannot process gz file '%s' from '%s' : %s" % | |
387 | + (local_gzip_file, url, e)) | |
388 | + # Sometimes, the downloaded gz is corrupted, and CRC checks fail. | |
389 | + # We want to delete the local gz file and try again next time. | |
390 | + removefile(local_gzip_file) | |
387 | 391 | if success: |
392 | + local_netc_files.append(local_netc_file) | |
388 | 393 | log.debug("Unzipped '%s'." % local_gzip_file) |
389 | 394 | |
390 | 395 | return local_netc_files |
... | ... | @@ -435,6 +440,8 @@ def get_data_for_target(target_config, started_at, stopped_at): |
435 | 440 | cdf_handle = Dataset(orbit_file, "r", format="NETCDF4") |
436 | 441 | times = cdf_handle.variables['Time'] # YYYY DOY HH MM SS .ms |
437 | 442 | data_hee = cdf_handle.variables['HEE'] |
443 | + log.debug("%s: aggregating data from '%s'..." % | |
444 | + (target_config['name'], orbit_file)) | |
438 | 445 | for time, datum_hee in zip(times, data_hee): |
439 | 446 | dtime = datetime_from_list(time) |
440 | 447 | if started_at <= dtime <= stopped_at: |
... | ... | @@ -523,6 +530,7 @@ def generate_csv_file_if_needed(target_slug, started_at, stopped_at): |
523 | 530 | stopped_at=stopped_at)) |
524 | 531 | log.info("Generation of '%s' done." % filename) |
525 | 532 | except Exception as e: |
533 | + log.error(e) | |
526 | 534 | if isfile(local_csv_file): |
527 | 535 | log.warn("Removing failed CSV '%s'..." % local_csv_file) |
528 | 536 | removefile(local_csv_file) | ... | ... |
web/static/img/target/rosetta_128.png
web/static/js/swapp.js
... | ... | @@ -742,7 +742,14 @@ |
742 | 742 | }; |
743 | 743 | Orbits.prototype.initOrbiter = function(slug, config, data){ |
744 | 744 | var orbit_ellipse, orbiter, orbit_line, orbit_section, this$ = this; |
745 | - console.info("Initializing orbit of " + config.name + "…"); | |
745 | + this.data[slug] = data; | |
746 | + this.orbiters[slug] = config; | |
747 | + if (data.length) { | |
748 | + console.info("Initializing orbit of " + config.name + "…"); | |
749 | + } else { | |
750 | + console.warn("No orbit data for " + config.name + "…"); | |
751 | + return; | |
752 | + } | |
746 | 753 | if (slug in this.orbitersElements) { |
747 | 754 | throw new Error("Second init of " + slug); |
748 | 755 | } |
... | ... | @@ -755,8 +762,6 @@ |
755 | 762 | return this$.yScale(d.x); |
756 | 763 | }); |
757 | 764 | orbit_section = this.plotWrapper.append('path').datum(data).classed('orbit orbit_section', true); |
758 | - this.data[slug] = data; | |
759 | - this.orbiters[slug] = config; | |
760 | 765 | this.orbitersElements[slug] = { |
761 | 766 | orbiter: orbiter, |
762 | 767 | orbit_ellipse: orbit_ellipse, |
... | ... | @@ -771,6 +776,9 @@ |
771 | 776 | return this; |
772 | 777 | }; |
773 | 778 | Orbits.prototype.showOrbiter = function(slug){ |
779 | + if (!this.data[slug].length) { | |
780 | + return; | |
781 | + } | |
774 | 782 | this.orbiters[slug].hidden = false; |
775 | 783 | this.orbitersElements[slug].orbiter.style("display", null); |
776 | 784 | this.orbitersElements[slug].orbit_ellipse.style("display", null); |
... | ... | @@ -778,6 +786,9 @@ |
778 | 786 | return this.resize(true); |
779 | 787 | }; |
780 | 788 | Orbits.prototype.hideOrbiter = function(slug){ |
789 | + if (!this.data[slug].length) { | |
790 | + return; | |
791 | + } | |
781 | 792 | this.orbiters[slug].hidden = true; |
782 | 793 | this.orbitersElements[slug].orbiter.style("display", "none"); |
783 | 794 | this.orbitersElements[slug].orbit_ellipse.style("display", "none"); |
... | ... | @@ -832,8 +843,12 @@ |
832 | 843 | return this; |
833 | 844 | }; |
834 | 845 | Orbits.prototype.resizeOrbiter = function(slug, config, width, height, animate){ |
835 | - var tt, el, orbit_section, t, a, b, c, cx, cy, orbit_ellipse; | |
846 | + var data, tt, el, orbit_section, t, a, b, c, cx, cy, orbit_ellipse; | |
836 | 847 | animate == null && (animate = false); |
848 | + data = this.data[slug]; | |
849 | + if (!data.length) { | |
850 | + return; | |
851 | + } | |
837 | 852 | console.debug("Resizing orbit of " + slug + "…"); |
838 | 853 | tt = this.svg.transition().duration(750); |
839 | 854 | el = this.orbitersElements[slug]; |
... | ... | @@ -861,6 +876,9 @@ |
861 | 876 | var data, el, t; |
862 | 877 | animate == null && (animate = false); |
863 | 878 | data = this.data[slug]; |
879 | + if (!data.length) { | |
880 | + return; | |
881 | + } | |
864 | 882 | datum == null && (datum = this.lastOrbiterData[slug]); |
865 | 883 | datum == null && (datum = data[data.length - 1]); |
866 | 884 | this.lastOrbiterData[slug] = datum; |
... | ... | @@ -896,29 +914,33 @@ |
896 | 914 | return this; |
897 | 915 | }; |
898 | 916 | Orbits.prototype.resizeDomain = function(started_at, stopped_at){ |
899 | - var slug, ref$, config, el, data, results$ = []; | |
917 | + var slug, ref$, config, el, data; | |
900 | 918 | for (slug in ref$ = this.orbiters) { |
901 | 919 | config = ref$[slug]; |
902 | 920 | el = this.orbitersElements[slug]; |
903 | 921 | data = this.data[slug].filter(fn$); |
922 | + if (!data.length) { | |
923 | + return; | |
924 | + } | |
904 | 925 | el['orbit_section'].datum(data); |
905 | - results$.push(el['orbit_section'].attr('d', el['orbit_line'])); | |
926 | + el['orbit_section'].attr('d', el['orbit_line']); | |
906 | 927 | } |
907 | - return results$; | |
908 | 928 | function fn$(d){ |
909 | 929 | var ref$; |
910 | 930 | return started_at <= (ref$ = d.t) && ref$ <= stopped_at; |
911 | 931 | } |
912 | 932 | }; |
913 | 933 | Orbits.prototype.resetZoom = function(){ |
914 | - var slug, ref$, config, el, results$ = []; | |
934 | + var slug, ref$, config, el; | |
915 | 935 | for (slug in ref$ = this.orbiters) { |
916 | 936 | config = ref$[slug]; |
917 | 937 | el = this.orbitersElements[slug]; |
938 | + if (!this.data[slug].length) { | |
939 | + return; | |
940 | + } | |
918 | 941 | el['orbit_section'].datum(this.data[slug]); |
919 | - results$.push(el['orbit_section'].attr('d', el['orbit_line'])); | |
942 | + el['orbit_section'].attr('d', el['orbit_line']); | |
920 | 943 | } |
921 | - return results$; | |
922 | 944 | }; |
923 | 945 | return Orbits; |
924 | 946 | }()); | ... | ... |
web/static/js/swapp.ls
... | ... | @@ -700,7 +700,15 @@ export class Orbits |
700 | 700 | @resize() |
701 | 701 | |
702 | 702 | initOrbiter: (slug, config, data) -> |
703 | - console.info "Initializing orbit of #{config.name}…" | |
703 | + @data[slug] = data | |
704 | + @orbiters[slug] = config | |
705 | + | |
706 | + if data.length | |
707 | + console.info "Initializing orbit of #{config.name}…" | |
708 | + else | |
709 | + console.warn "No orbit data for #{config.name}…" | |
710 | + return | |
711 | + | |
704 | 712 | if slug of @orbitersElements then throw new Error("Second init of #{slug}") |
705 | 713 | |
706 | 714 | # The order is important, as it will define the default z-order |
... | ... | @@ -719,8 +727,6 @@ export class Orbits |
719 | 727 | .datum(data) |
720 | 728 | .classed('orbit orbit_section', true) |
721 | 729 | |
722 | - @data[slug] = data | |
723 | - @orbiters[slug] = config | |
724 | 730 | @orbitersElements[slug] = |
725 | 731 | orbiter: orbiter |
726 | 732 | orbit_ellipse: orbit_ellipse |
... | ... | @@ -737,6 +743,7 @@ export class Orbits |
737 | 743 | this |
738 | 744 | |
739 | 745 | showOrbiter: (slug) -> |
746 | + if not @data[slug].length then return | |
740 | 747 | @orbiters[slug].hidden = false |
741 | 748 | @orbitersElements[slug].orbiter.style("display", null) |
742 | 749 | @orbitersElements[slug].orbit_ellipse.style("display", null) |
... | ... | @@ -744,6 +751,7 @@ export class Orbits |
744 | 751 | @resize(true) |
745 | 752 | |
746 | 753 | hideOrbiter: (slug) -> |
754 | + if not @data[slug].length then return | |
747 | 755 | @orbiters[slug].hidden = true |
748 | 756 | @orbitersElements[slug].orbiter.style("display", "none") |
749 | 757 | @orbitersElements[slug].orbit_ellipse.style("display", "none") |
... | ... | @@ -797,6 +805,8 @@ export class Orbits |
797 | 805 | this |
798 | 806 | |
799 | 807 | resizeOrbiter: (slug, config, width, height, animate = false) -> |
808 | + data = @data[slug] | |
809 | + if not data.length then return | |
800 | 810 | console.debug("Resizing orbit of #{slug}…") |
801 | 811 | |
802 | 812 | tt = @svg.transition().duration(750) |
... | ... | @@ -830,6 +840,7 @@ export class Orbits |
830 | 840 | |
831 | 841 | repositionOrbiter: (slug, datum, animate = false) -> |
832 | 842 | data = @data[slug] |
843 | + if not data.length then return | |
833 | 844 | datum ?= @lastOrbiterData[slug] |
834 | 845 | datum ?= data[data.length - 1] |
835 | 846 | @lastOrbiterData[slug] = datum |
... | ... | @@ -859,12 +870,14 @@ export class Orbits |
859 | 870 | for slug, config of @orbiters |
860 | 871 | el = @orbitersElements[slug] |
861 | 872 | data = @data[slug].filter (d) -> started_at <= d.t <= stopped_at |
873 | + if not data.length then return | |
862 | 874 | el['orbit_section'].datum(data) |
863 | 875 | el['orbit_section'].attr('d', el['orbit_line']) |
864 | 876 | |
865 | 877 | resetZoom: -> |
866 | 878 | for slug, config of @orbiters |
867 | 879 | el = @orbitersElements[slug] |
880 | + if not @data[slug].length then return | |
868 | 881 | el['orbit_section'].datum(@data[slug]) |
869 | 882 | el['orbit_section'].attr('d', el['orbit_line']) |
870 | 883 | ... | ... |
web/view/home.html.jinja2
... | ... | @@ -116,7 +116,7 @@ |
116 | 116 | <section id="orbits"></section> |
117 | 117 | <div class="plots-actions plots-buttons"> |
118 | 118 | <button id="download" class="mdl-button mdl-button--raised mdl-button--primary" |
119 | - title="Download the CSV raw data for each target in a tarball."> | |
119 | + title="Download the raw data for each selected target and parameters."> | |
120 | 120 | Download |
121 | 121 | </button> |
122 | 122 | <button id="samp" class="samp mdl-button mdl-button--raised mdl-button--primary mdl-button--disabled" |
... | ... | @@ -535,7 +535,7 @@ var configuration = { |
535 | 535 | orbits_container: '#orbits', |
536 | 536 | api: { |
537 | 537 | 'data_for_interval': "{{ request.url_root }}<target>_<started_at>_<stopped_at>.csv", |
538 | - 'download': "{{ request.url_root }}<targets>_<started_at>_<stopped_at>.tar.gz", | |
538 | + 'download': "{{ request.url_root }}<targets>_<started_at>_<stopped_at>.nc", | |
539 | 539 | 'samp': "{{ request.url_root }}<targets>_<params>_<started_at>_<stopped_at>.nc" |
540 | 540 | }, |
541 | 541 | sun: { | ... | ... |