ooi-data-explorations 0.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. __init__.py +0 -0
  2. examples/__init__.py +0 -0
  3. examples/camds/uncabled.ipynb +412 -0
  4. examples/ctdbp/__init__.py +0 -0
  5. examples/ctdbp/combine_mooring_ctdbp.py +212 -0
  6. examples/ctdbp/recovered_host_ctdbp.py +52 -0
  7. examples/ctdbp/recovered_inst_ctdbp.py +51 -0
  8. examples/ctdbp/telemetered_ctdbp.py +52 -0
  9. examples/ctdpf/__init__.py +0 -0
  10. examples/ctdpf/cspp_ctdpf.py +47 -0
  11. examples/dosta/__init__.py +0 -0
  12. examples/dosta/combine_mooring_dosta.py +210 -0
  13. examples/dosta/recovered_host_ctdbp_dosta.py +52 -0
  14. examples/dosta/recovered_host_solo_dosta.py +52 -0
  15. examples/dosta/recovered_inst_ctdbp_dosta.py +52 -0
  16. examples/dosta/telemetered_ctdbp_dosta.py +52 -0
  17. examples/dosta/telemetered_solo_dosta.py +52 -0
  18. examples/flort/__init__.py +0 -0
  19. examples/flort/cabled_profiler_flort.py +39 -0
  20. examples/flort/cspp_flort.py +50 -0
  21. examples/flort/recovered_host_flort.py +51 -0
  22. examples/flort/recovered_inst_flort.py +51 -0
  23. examples/flort/telemetered_flort.py +51 -0
  24. examples/flort/wfp_flort.py +50 -0
  25. examples/metbk/__init__.py +0 -0
  26. examples/metbk/recovered_host_metbk.py +48 -0
  27. examples/metbk/telemetered_bulk_flux.py +50 -0
  28. examples/metbk/telemetered_metbk.py +50 -0
  29. examples/notebooks/dosta/ooiea_cabled_dosta_processing.ipynb +1784 -0
  30. examples/notebooks/dosta/ooiea_dosta_annotations_hitl_qc.ipynb +220 -0
  31. examples/notebooks/dosta/ooiea_dosta_quality_assessments.ipynb +786 -0
  32. examples/notebooks/dosta/ooiea_uncabled_dosta_data.ipynb +368 -0
  33. examples/notebooks/fdchp/.gitignore +1 -0
  34. examples/notebooks/fdchp/FdchpExample.ipynb +552 -0
  35. examples/notebooks/fdchp/ProcessFdchpRaw.ipynb +6235 -0
  36. examples/notebooks/ifcb/combining_ifcb_and_environmental.ipynb +1619 -0
  37. examples/notebooks/m2m_explorations.ipynb +2313 -0
  38. examples/notebooks/optaa/acs137_20210302_pre_deployment_adat.csv +84 -0
  39. examples/notebooks/optaa/acs137_20210302_pre_deployment_cdat.csv +84 -0
  40. examples/notebooks/optaa/process_kdata_cabled_profiler_ce04osps.ipynb +3863 -0
  41. examples/notebooks/optaa/process_kdata_cabled_profiler_rs01sbps.ipynb +3902 -0
  42. examples/notebooks/optaa/process_kdata_optaa.ipynb +2283 -0
  43. examples/notebooks/optaa/process_thredds_optaa.ipynb +2292 -0
  44. examples/notebooks/optaa/process_thredds_optaa_cspp.ipynb +1078 -0
  45. examples/notebooks/optaa/processing_workflow_using_the_cspp_optaa.ipynb +5364 -0
  46. examples/notebooks/optaa/when_things_go_bad.ipynb +2087 -0
  47. examples/notebooks/pco2a/carbonate_climatologies.ipynb +686 -0
  48. examples/notebooks/pco2a/regional_pco2_comparisons.png +0 -0
  49. examples/notebooks/pco2a/regional_ph_comparisons.png +0 -0
  50. examples/notebooks/pco2w/PCO2_Calculations.ipynb +2880 -0
  51. examples/notebooks/pco2w/ce01issm_pco2w_C0119_converted_2019-10-31.csv +2357 -0
  52. examples/notebooks/phsen/creating_annotations.ipynb +1983 -0
  53. examples/nutnr/__init__.py +0 -0
  54. examples/nutnr/cspp_nutnr.py +47 -0
  55. examples/nutnr/recovered_host_nutnr.py +52 -0
  56. examples/nutnr/recovered_inst_nutnr.py +52 -0
  57. examples/nutnr/telemetered_nutnr.py +52 -0
  58. examples/optaa/__init__.py +0 -0
  59. examples/optaa/cabled_benthic_optaa.py +48 -0
  60. examples/optaa/cabled_profiler_optaa.py +48 -0
  61. examples/optaa/cspp_optaa.py +49 -0
  62. examples/optaa/recovered_host_optaa.py +49 -0
  63. examples/optaa/telemetered_optaa.py +54 -0
  64. examples/pco2a/__init__.py +0 -0
  65. examples/pco2a/recovered_host_pco2a.py +59 -0
  66. examples/pco2a/telemetered_pco2a.py +57 -0
  67. examples/pco2w/__init__.py +0 -0
  68. examples/pco2w/combine_mooring_pco2w.py +195 -0
  69. examples/pco2w/recovered_host_pco2w.py +52 -0
  70. examples/pco2w/recovered_inst_pco2w.py +51 -0
  71. examples/pco2w/streamed_pco2w.py +52 -0
  72. examples/pco2w/telemetered_pco2w.py +52 -0
  73. examples/phsen/__init__.py +0 -0
  74. examples/phsen/combine_mooring_phsen.py +180 -0
  75. examples/phsen/imodem_phsen.py +51 -0
  76. examples/phsen/recovered_host_phsen.py +52 -0
  77. examples/phsen/recovered_inst_phsen.py +51 -0
  78. examples/phsen/streamed_phsen.py +52 -0
  79. examples/phsen/telemetered_phsen.py +52 -0
  80. examples/spkir/__init__.py +0 -0
  81. examples/spkir/cspp_spkir.py +47 -0
  82. examples/spkir/recovered_host_spkir.py +47 -0
  83. examples/spkir/telemetered_spkir.py +45 -0
  84. examples/tensorflow/sealion_detection_example.ipynb +708 -0
  85. examples/tensorflow/sealion_training_example.ipynb +371 -0
  86. examples/vel3d/__init__.py +0 -0
  87. examples/vel3d/recovered_host_vel3d.py +48 -0
  88. examples/vel3d/recovered_wfp_vel3d.py +42 -0
  89. examples/vel3d/telemetered_vel3d.py +47 -0
  90. examples/vel3d/telemetered_wfp_vel3d.py +42 -0
  91. examples/velpt/__init__.py +0 -0
  92. examples/velpt/cspp_velpt.py +47 -0
  93. examples/velpt/recovered_host_velpt.py +45 -0
  94. examples/velpt/recovered_inst_velpt.py +39 -0
  95. examples/velpt/telemetered_velpt.py +45 -0
  96. examples/water_quality/__init__.py +0 -0
  97. examples/water_quality/wqx_ctdbp.py +147 -0
  98. examples/water_quality/wqx_dosta.py +145 -0
  99. examples/water_quality/wqx_flort.py +158 -0
  100. examples/water_quality/wqx_nutnr.py +146 -0
  101. examples/water_quality/wqx_phsen.py +144 -0
  102. examples/wavss/__init__.py +0 -0
  103. examples/wavss/recovered_host_wavss_directional.py +38 -0
  104. examples/wavss/recovered_host_wavss_summary.py +37 -0
  105. examples/wavss/telemetered_wavss_directional.py +42 -0
  106. examples/wavss/telemetered_wavss_summary.py +42 -0
  107. ooi_data_explorations/__init__.py +7 -0
  108. ooi_data_explorations/bottles.py +264 -0
  109. ooi_data_explorations/cabled/__init__.py +0 -0
  110. ooi_data_explorations/cabled/process_flort.py +157 -0
  111. ooi_data_explorations/cabled/process_optaa.py +531 -0
  112. ooi_data_explorations/cabled/process_phsen.py +176 -0
  113. ooi_data_explorations/calibrations.py +64 -0
  114. ooi_data_explorations/combine_data.py +238 -0
  115. ooi_data_explorations/common.py +1381 -0
  116. ooi_data_explorations/construct_urls.py +210 -0
  117. ooi_data_explorations/data_request.py +272 -0
  118. ooi_data_explorations/m2m_urls.yml +12186 -0
  119. ooi_data_explorations/profilers.py +296 -0
  120. ooi_data_explorations/qartod/__init__.py +0 -0
  121. ooi_data_explorations/qartod/cgsn/scripts/CTDBP.ipynb +2878 -0
  122. ooi_data_explorations/qartod/cgsn/scripts/CTDMO.ipynb +2894 -0
  123. ooi_data_explorations/qartod/cgsn/scripts/CTDPF.ipynb +1129 -0
  124. ooi_data_explorations/qartod/cgsn/scripts/DOSTA.ipynb +1156 -0
  125. ooi_data_explorations/qartod/cgsn/scripts/FLORD.ipynb +1267 -0
  126. ooi_data_explorations/qartod/cgsn/scripts/FLORT.ipynb +1320 -0
  127. ooi_data_explorations/qartod/cgsn/scripts/METBK.ipynb +1299 -0
  128. ooi_data_explorations/qartod/cgsn/scripts/NUTNR.ipynb +1550 -0
  129. ooi_data_explorations/qartod/cgsn/scripts/PAR.ipynb +1101 -0
  130. ooi_data_explorations/qartod/cgsn/scripts/PCO2A.ipynb +1069 -0
  131. ooi_data_explorations/qartod/cgsn/scripts/PCO2W.ipynb +1090 -0
  132. ooi_data_explorations/qartod/cgsn/scripts/PHSEN.ipynb +1099 -0
  133. ooi_data_explorations/qartod/cgsn/scripts/WAVSS.ipynb +1301 -0
  134. ooi_data_explorations/qartod/cgsn/scripts/wavss_sensor_ranges.yaml +41 -0
  135. ooi_data_explorations/qartod/climatology.py +211 -0
  136. ooi_data_explorations/qartod/discrete_samples.py +196 -0
  137. ooi_data_explorations/qartod/endurance/__init__.py +0 -0
  138. ooi_data_explorations/qartod/endurance/qartod_ce_ctdbp.py +205 -0
  139. ooi_data_explorations/qartod/endurance/qartod_ce_ctdpf.py +195 -0
  140. ooi_data_explorations/qartod/endurance/qartod_ce_dofst.py +199 -0
  141. ooi_data_explorations/qartod/endurance/qartod_ce_dosta.py +271 -0
  142. ooi_data_explorations/qartod/endurance/qartod_ce_flort.py +324 -0
  143. ooi_data_explorations/qartod/endurance/qartod_ce_gliders.py +135 -0
  144. ooi_data_explorations/qartod/endurance/qartod_ce_metbk.py +258 -0
  145. ooi_data_explorations/qartod/endurance/qartod_ce_nutnr.py +287 -0
  146. ooi_data_explorations/qartod/endurance/qartod_ce_parad.py +209 -0
  147. ooi_data_explorations/qartod/endurance/qartod_ce_pco2a.py +218 -0
  148. ooi_data_explorations/qartod/endurance/qartod_ce_pco2w.py +166 -0
  149. ooi_data_explorations/qartod/endurance/qartod_ce_phsen.py +194 -0
  150. ooi_data_explorations/qartod/endurance/qartod_ce_presf.py +193 -0
  151. ooi_data_explorations/qartod/endurance/qartod_ce_spkir.py +231 -0
  152. ooi_data_explorations/qartod/endurance/qartod_ce_vel3d.py +298 -0
  153. ooi_data_explorations/qartod/endurance/qartod_ce_velpt.py +239 -0
  154. ooi_data_explorations/qartod/endurance/qartod_ce_wavss.py +187 -0
  155. ooi_data_explorations/qartod/endurance/reports/__init__.py +0 -0
  156. ooi_data_explorations/qartod/endurance/reports/reports_ce_ctdbp.py +397 -0
  157. ooi_data_explorations/qartod/endurance/reports/reports_ce_nutnr.py +395 -0
  158. ooi_data_explorations/qartod/endurance/reports/reports_ce_phsen.py +406 -0
  159. ooi_data_explorations/qartod/gross_range.py +281 -0
  160. ooi_data_explorations/qartod/plotting.py +145 -0
  161. ooi_data_explorations/qartod/qc_processing.py +668 -0
  162. ooi_data_explorations/qartod/rca/decimate.py +211 -0
  163. ooi_data_explorations/qartod/rca/parameterMap.csv +13 -0
  164. ooi_data_explorations/qartod/rca/qartodTests.csv +3 -0
  165. ooi_data_explorations/qartod/rca/qartod_rca.py +344 -0
  166. ooi_data_explorations/qartod/rca/siteParameters.csv +41 -0
  167. ooi_data_explorations/qartod/reporting.py +92 -0
  168. ooi_data_explorations/qartod/table_checker.py +270 -0
  169. ooi_data_explorations/uncabled/__init__.py +0 -0
  170. ooi_data_explorations/uncabled/ooim2m_cspp.py +280 -0
  171. ooi_data_explorations/uncabled/process_adcp.py +452 -0
  172. ooi_data_explorations/uncabled/process_ctdbp.py +216 -0
  173. ooi_data_explorations/uncabled/process_ctdpf.py +144 -0
  174. ooi_data_explorations/uncabled/process_dofst.py +215 -0
  175. ooi_data_explorations/uncabled/process_dosta.py +547 -0
  176. ooi_data_explorations/uncabled/process_fdchp.py +1141 -0
  177. ooi_data_explorations/uncabled/process_flord.py +306 -0
  178. ooi_data_explorations/uncabled/process_flort.py +607 -0
  179. ooi_data_explorations/uncabled/process_metbk.py +384 -0
  180. ooi_data_explorations/uncabled/process_mopak.py +1621 -0
  181. ooi_data_explorations/uncabled/process_nutnr.py +835 -0
  182. ooi_data_explorations/uncabled/process_optaa.py +843 -0
  183. ooi_data_explorations/uncabled/process_parad.py +259 -0
  184. ooi_data_explorations/uncabled/process_pco2a.py +289 -0
  185. ooi_data_explorations/uncabled/process_pco2w.py +491 -0
  186. ooi_data_explorations/uncabled/process_phsen.py +595 -0
  187. ooi_data_explorations/uncabled/process_spkir.py +394 -0
  188. ooi_data_explorations/uncabled/process_turbd.py +99 -0
  189. ooi_data_explorations/uncabled/process_vel3d.py +1347 -0
  190. ooi_data_explorations/uncabled/process_velpt.py +521 -0
  191. ooi_data_explorations/uncabled/process_wavss.py +540 -0
  192. ooi_data_explorations-0.2.3.dist-info/METADATA +457 -0
  193. ooi_data_explorations-0.2.3.dist-info/RECORD +197 -0
  194. ooi_data_explorations-0.2.3.dist-info/WHEEL +4 -0
  195. ooi_data_explorations-0.2.3.dist-info/licenses/LICENSE.txt +21 -0
  196. setup.py +59 -0
  197. version.py +1 -0
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ @author Christopher Wingard
5
+ @brief Creates the YAML file with the information needed to construct URLs for M2M requests
6
+ """
7
+ import requests
8
+
9
+ from ooi_data_explorations.common import AUTH, BASE_URL
10
+ from ooi_data_explorations.common import list_nodes, list_sensors, list_methods, list_streams
11
+
12
+ # Organize the nodes into assemblies common across all the arrays. Helps to better organize the data, taking all the
13
+ # different names used in OOI and collapsing them down to a more coherent, and logical grouping. This list is
14
+ # deliberately non-inclusive of all possible nodes, as some are either specific to control systems or have data not
15
+ # readily accessed through the M2M system.
16
+ ASSEMBLY = {
17
+ 'buoy': ['SBD11', 'SBD12', 'SBD17'],
18
+ 'midwater': ['PC01A', 'PC01B', 'PC03A', 'RID16', 'RID26', 'RID27', 'RII01', 'RII11', 'RIM01', 'RIS01'],
19
+ 'seafloor': [
20
+ 'LJ01A', 'LJ01B', 'LJ01C', 'LJ01D', 'LJ03A', 'MFD35', 'MFD37', 'MJ01A', 'MJ01B', 'MJ03A', 'MJ03B',
21
+ 'MJ03C', 'MJ03D', 'MJ03E', 'MJ03F', 'PN03B'],
22
+ 'glider': [
23
+ 'GL247', 'GL276', 'GL311', 'GL312', 'GL319', 'GL320', 'GL326', 'GL327', 'GL335', 'GL336', 'GL339',
24
+ 'GL340', 'GL361', 'GL362', 'GL363', 'GL364', 'GL365', 'GL374', 'GL375', 'GL376', 'GL379', 'GL380',
25
+ 'GL381', 'GL382', 'GL383', 'GL384', 'GL386', 'GL387', 'GL388', 'GL389', 'GL453', 'GL469', 'GL470',
26
+ 'GL477', 'GL478', 'GL484', 'GL485', 'GL486', 'GL493', 'GL494', 'GL495', 'GL496', 'GL514', 'GL523',
27
+ 'GL524', 'GL525', 'GL537', 'GL538', 'GL559', 'GL560', 'GL561', 'PG514', 'PG515', 'PG528', 'PG562',
28
+ 'PG563', 'PG564', 'PG565', 'PG566', 'PG575', 'PG578', 'PG580', 'PG583'
29
+ ],
30
+ 'auv': ['A6263', 'A6264'],
31
+ 'profiler': ['WFP01', 'WFP02', 'WFP03', 'SP001', 'SF01A', 'SF01B', 'SF03A', 'DP01A', 'DP01B', 'DP03A']
32
+ }
33
+
34
+ # Assemblies can be further categorized by a sub-assembly type if needed. Otherwise, the code and assembly type are
35
+ # the same. This helps to differentiate times where more than one assembly type is present at a site and we need to
36
+ # further distinguish between them.
37
+ SUBASSEMBLY = {
38
+ 'nsif': ['RID16', 'RID26', 'RID27'],
39
+ 'riser': ['RII01', 'RII11', 'RIM01'],
40
+ 'sphere': ['RIS01'],
41
+ '200m': ['PC01A', 'PC01B', 'PC03A'],
42
+ 'mfn': ['MFD35', 'MFD37'],
43
+ 'bep': ['LJ01C', 'LJ01D'],
44
+ 'low-power': ['LJ01A', 'LJ01B', 'LJ03A'],
45
+ 'medium-power': ['MJ01A', 'MJ01B', 'MJ03A', 'MJ03B', 'MJ03C', 'MJ03D', 'MJ03E', 'MJ03F'],
46
+ 'coastal-glider': [
47
+ 'GL247', 'GL276', 'GL311', 'GL312', 'GL319', 'GL320', 'GL326', 'GL327', 'GL335', 'GL336', 'GL339', 'GL340',
48
+ 'GL361', 'GL362', 'GL363', 'GL364', 'GL365', 'GL374', 'GL375', 'GL376', 'GL379', 'GL380', 'GL381', 'GL382',
49
+ 'GL383', 'GL384', 'GL386', 'GL387', 'GL388', 'GL389', 'GL514',
50
+ ],
51
+ 'global-glider': [
52
+ 'GL453', 'GL469', 'GL470', 'GL477', 'GL478', 'GL484', 'GL485', 'GL486', 'GL493', 'GL494', 'GL495', 'GL496',
53
+ 'GL523', 'GL524', 'GL525', 'GL537', 'GL538', 'GL559', 'GL560', 'GL561'
54
+ ],
55
+ 'profiling-glider': [
56
+ 'PG514', 'PG515', 'PG528', 'PG562', 'PG563', 'PG564', 'PG565', 'PG566', 'PG575', 'PG578', 'PG580', 'PG583'
57
+ ],
58
+ 'cspp': ['SP001'],
59
+ 'coastal-wfp': ['WFP01'],
60
+ 'global-wfp': ['WFP02', 'WFP03'],
61
+ 'cabled-wfp': ['DP01A', 'DP01B', 'DP03A'],
62
+ 'shallow-profiler': ['SF01A', 'SF01B', 'SF03A']
63
+ }
64
+
65
+ # List of allowed methods (there are some called bad_*, ignoring those)
66
+ METHODS = ['streamed', 'telemetered', 'recovered_host', 'recovered_inst', 'recovered_cspp', 'recovered_wfp']
67
+
68
+ # Sensor codes to exclude. These are either not science sensors (e.g. 00-DCLENG000), or not really accessible from the
69
+ # M2M system (e.g. cameras, hydrophones or seismometers).
70
+ SENSOR_EXCLUDES = [
71
+ 'AOABP', 'CAM', 'COVIS', 'ENG', 'FLOBN', 'HPIES', 'HYD', 'MASSP', 'OBS', 'OSMOI', 'OVRSR', 'PPSDNA', 'QNTSR',
72
+ 'RASFL', 'SCPRA', 'SCTAA', 'ZPLSCB',
73
+ ]
74
+
75
+ # Several streams were defined within OOI that are beyond core science needs. The terms below filter out those streams.
76
+ STREAM_EXCLUDES = [
77
+ 'metadata', 'diagnostics', 'abc_power', 'dcl_power', 'config', 'wave_burst', 'imodem_status',
78
+ 'imodem_start_time', 'cspp_dark_instrument', 'instrument_blank', 'adcp_engineering', 'adcp_ancillary',
79
+ 'adcp_transmit', 'dcl_fourier', 'dcl_mean_directional', 'dcl_motion', 'dcl_non_directional', 'echogram_data',
80
+ 'calibration', 'status', 'settings', 'hardware', 'adcp_pd0_beam_parsed', 'identification_string', 'clock_data',
81
+ 'battery_voltage', 'thermistor_voltage', 'dev1_data', 'data_record_cal', 'dark_sample', 'imodem_control',
82
+ 'imodem_power', 'offset', 'adcp_velocity_glider', 'nutnr_b_dcl_dark', 'nutnr_b_metadata', 'nutnr_b_dark',
83
+ 'prest_event_counter', 'prest_reference_oscillator', 'vel3d_b_engineering', 'tmpsf_engineering',
84
+ 'vadcp_5thbeam_pd0_beam_parsed', 'nutnr_a_test'
85
+ ]
86
+
87
+
88
+ # grab the entire OOI vocabulary dictionary
89
+ def get_vocabulary():
90
+ """
91
+ Uses the an API call to grab the entire vocabulary dictionary for all OOI sites, nodes and sensors
92
+
93
+ :return: json object with the site-node-sensor specific vocabulary
94
+ """
95
+ r = requests.get(BASE_URL + '12586/vocab', auth=(AUTH[0], AUTH[2]))
96
+ if r.status_code == requests.codes.ok:
97
+ return r.json()
98
+ else:
99
+ return None
100
+
101
+
102
+ VOCAB = get_vocabulary()
103
+
104
+
105
+ def add_site(site, url_file):
106
+ """
107
+ For an OOI site, assemble a curated list of all the instruments and data streams that are available for data
108
+ explorations. This file will create a YAML file per site. Additional HITL work is required to further clean-up and
109
+ check the list, and pruning streams and methods down to the core set of science sensors. The results of this work
110
+ is used to create the m2m_urls.yml file used in this python toolbox.
111
+
112
+ :param site: OOI 8 character site designation (e.g. CE01ISSM)
113
+ :param url_file: data file to save the results in.
114
+ :return: None, creates the data file.
115
+ """
116
+ sp = ' ' # create an explicit two-space indent string for the YAML file
117
+ # open the YAML file to store the results
118
+ with open(url_file, 'w') as f:
119
+ # create the header portion of the YAML, getting the array and site names from the API
120
+ site = site.upper()
121
+ for site_vocab in VOCAB:
122
+ if site_vocab.get('refdes') == site:
123
+ break
124
+
125
+ f.write('---\n{site}:\n'.format(site=site))
126
+ f.write('{sp}array: {array}\n'.format(sp=sp*1, array=site_vocab['tocL1']))
127
+ f.write('{sp}name: {name}\n'.format(sp=sp*1, name=site_vocab['tocL2']))
128
+ f.write('{sp}assembly:\n'.format(sp=sp*1))
129
+
130
+ # create a list of nodes for this site
131
+ nodes = list_nodes(site)
132
+
133
+ # for each node, if it is one of interest as defined above, create an assembly entry
134
+ for k, v in ASSEMBLY.items():
135
+ # find the nodes that correspond to this assembly
136
+ assembly = k
137
+ node = sorted(set(v) & set(nodes))
138
+
139
+ # skip if we don't have any nodes in this assembly
140
+ if not node:
141
+ continue
142
+
143
+ # if we have nodes, create the assembly entry
144
+ for node_vocab in VOCAB:
145
+ if node_vocab.get('refdes') == site + '-' + node[0]:
146
+ break
147
+
148
+ # for each node, create the list of sensors
149
+ for n in node:
150
+ f.write('{sp}- type: {assembly}\n'.format(sp=sp * 2, assembly=assembly))
151
+ f.write('{sp}name: {name}\n'.format(sp=sp * 3, name=node_vocab['tocL3']))
152
+ # if we need to further distinguish, add the assembly code name
153
+ for l, w in SUBASSEMBLY.items():
154
+ if n in w:
155
+ f.write('{sp}subassembly: {name}\n'.format(sp=sp * 3, name=l))
156
+ f.write('{sp}instrument:\n'.format(sp=sp * 3))
157
+ sensors = list_sensors(site, n)
158
+ sensors = filter_stream(sensors, SENSOR_EXCLUDES) # remove sensors of no interest
159
+
160
+ if not sensors:
161
+ continue
162
+
163
+ for sensor in sensors:
164
+ for sensor_vocab in VOCAB:
165
+ if sensor_vocab.get('refdes') == site + '-' + n + '-' + sensor:
166
+ break
167
+
168
+ instrument = (sensor[3:8]).lower()
169
+ f.write('{sp}- class: {instrument}\n'.format(sp=sp*4, instrument=instrument))
170
+ f.write('{sp}instrument_name: {name}\n'.format(sp=sp*5, name=sensor_vocab['instrument']))
171
+ f.write('{sp}instrument_model: {model}\n'.format(sp=sp*5, model=sensor_vocab['model']))
172
+ f.write('{sp}instrument_manufacturer: {manu}\n'.format(sp=sp*5, manu=sensor_vocab['manufacturer']))
173
+ f.write('{sp}mindepth: {mindepth}\n'.format(sp=sp*5, mindepth=sensor_vocab['mindepth']))
174
+ f.write('{sp}maxdepth: {maxdepth}\n'.format(sp=sp*5, maxdepth=sensor_vocab['maxdepth']))
175
+ f.write('{sp}node: {node}\n'.format(sp=sp*5, node=n))
176
+ f.write('{sp}sensor: {sensor}\n'.format(sp=sp*5, sensor=sensor))
177
+ f.write('{sp}stream:\n'.format(sp=sp*5))
178
+
179
+ methods = list_methods(site, n, sensor)
180
+ if not methods:
181
+ f.write('{sp}unknown: null\n'.format(sp=sp*6,))
182
+ continue
183
+
184
+ for method in methods:
185
+ if method in METHODS:
186
+ streams = list_streams(site, n, sensor, method)
187
+ streams = filter_stream(streams, STREAM_EXCLUDES)
188
+ if len(streams) == 1:
189
+ f.write('{sp}{method}: {streams}\n'.format(sp=sp*6, method=method,
190
+ streams=streams[0]))
191
+ else:
192
+ f.write('{sp}{method}:\n'.format(sp=sp*6, method=method))
193
+ for stream in streams:
194
+ f.write('{sp}- {stream}\n'.format(sp=sp*7, stream=stream))
195
+
196
+
197
+ def filter_stream(streams, excludes):
198
+ """
199
+ Uses a list of keywords to remove sensors or streams from the list returned by OOI Net.
200
+
201
+ :param streams: list of sensor or streams returned from OOI Net
202
+ :param excludes: list of keywords to use in pruning the list
203
+ :return: a cleaned, pruned list
204
+ """
205
+ clean = []
206
+ for stream in streams:
207
+ if not any(sub in stream for sub in excludes):
208
+ clean.append(stream)
209
+
210
+ return clean
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ import os
4
+ import numpy as np
5
+ import pytz
6
+ import xarray as xr
7
+ import dateutil.parser as parser
8
+
9
+ from ooi_data_explorations import M2M_URLS
10
+ from ooi_data_explorations.common import dr_inputs, m2m_request, m2m_collect, get_deployment_dates
11
+
12
+
13
+ def filter_urls(site, assembly, instrument, method):
14
+ """
15
+ Takes the M2M_URLS dictionary and searches for the instrument of interest
16
+ as defined by the site code, assembly type, instrument class, and data
17
+ delivery method to return the OOI specific site, node and stream names
18
+ needed to request the data.
19
+
20
+ :param site: OOI eight letter site code (e.g. CE04OSPS for the Oregon
21
+ Offshore Shallow Profiler)
22
+ :param assembly: Assembly grouping name (e.g. midwater for the 200 m
23
+ Platform)
24
+ :param instrument: The instrument class name (e.g. phsen for the
25
+ SAMI2-pH sensor)
26
+ :param method: The data delivery method (e.g. streamed for cabled
27
+ streaming data)
28
+
29
+ :return node: The OOI specific node code(s) for the assembly
30
+ :return sensor: The OOI specific sensor code(s) for the instrument class
31
+ :return stream: The OOI specific stream name(s) for the site, node, sensor
32
+ and delivery method combination
33
+ """
34
+ # Use the site, assembly, instrument, and data delivery method to determine the OOI specific site, node, sensor,
35
+ # method and stream
36
+ node = []
37
+ sensor = []
38
+ stream = []
39
+
40
+ # pare the larger dictionary down to the site of interest and check if a valid site was used
41
+ m2m_urls = M2M_URLS.get(site.upper())
42
+ if not m2m_urls:
43
+ raise SyntaxError('Unknown site code: %s' % site)
44
+
45
+ # make sure the correct data delivery method was specified
46
+ if method not in ['streamed', 'telemetered', 'recovered_host', 'recovered_inst', 'recovered_cspp', 'recovered_wfp']:
47
+ raise SyntaxError('Unknown data delivery method: %s' % method)
48
+
49
+ # find the instrument(s) of interest in the assembly group
50
+ for grouping in m2m_urls.get('assembly'):
51
+ if grouping.get('type') == assembly or grouping.get('subassembly') == assembly:
52
+ for instrmt in grouping['instrument']:
53
+ if instrmt['class'] == instrument:
54
+ node.append(instrmt.node)
55
+ sensor.append(instrmt.sensor)
56
+ stream.append(instrmt.stream.get(method))
57
+
58
+ # check to see if we were able to find the system of interest
59
+ if not stream:
60
+ raise RuntimeWarning('Instrument defined by %s-%s-%s-%s cannot be found.' % (site, assembly, instrument,
61
+ method))
62
+
63
+ # return the OOI specific names for the node(s), sensor(s) and stream(s)
64
+ return node, sensor, stream
65
+
66
+
67
+ def data_request(site, assembly, instrument, method, **kwargs):
68
+ """
69
+ Requests data via the OOI M2M API using the site code, assembly type,
70
+ instrument class and data delivery method as defined in the m2m_urls.yml to
71
+ construct the OOI specific data request.
72
+
73
+ :param site: OOI site code as an 8 character string
74
+ :param assembly: The assembly type where the instrument is located
75
+ :param instrument: the OOI instrument class name for the instrument of
76
+ interest
77
+ :param method: The data delivery method for the system of interest
78
+ :param kwargs: Takes the following optional keyword arguments:
79
+
80
+ start: Starting date/time for the data request in a dateutil.parser
81
+ recognizable form. If ``None``, the default, the beginning of the
82
+ data record will be used
83
+ stop: Ending date/time for the data request in a dateutil.parser
84
+ recognizable form If ``None``, the default, the end of the data
85
+ record will be used
86
+ deploy: Use the deployment number, entered as an integer, to set the
87
+ starting and ending dates If ``None``, the default, the starting
88
+ and ending dates are used. If you enter both, the deployment number
89
+ will take priority in setting the start and end dates
90
+ aggregate: In cases where more than one instance of an instrument class
91
+ is part of an assembly, will collect all of the data if the integer
92
+ value entered is ``0``, or the specific instance of the instrument
93
+ is requested if any value greater than ``0`` is used. If ``None``,
94
+ the default, the first instance of an instrument will be used.
95
+
96
+ :return data: Returns the request data as an xarray dataset for further analysis
97
+ """
98
+ # setup inputs to the function, make sure case is correct
99
+ site = site.upper()
100
+ assembly = assembly.lower()
101
+ instrument = instrument.lower()
102
+ method = method.lower()
103
+
104
+ # parse the keyword arguments
105
+ start = None
106
+ stop = None
107
+ deploy = None
108
+ aggregate = None
109
+ for key, value in kwargs.items():
110
+ if key not in ['start', 'stop', 'deploy', 'aggregate']:
111
+ raise KeyError('Unknown keyword (%s) argument.' % key)
112
+ else:
113
+ if key == 'start':
114
+ start = value
115
+ if key == 'stop':
116
+ stop = value
117
+ if key == 'deploy':
118
+ deploy = value
119
+ if key == 'aggregate':
120
+ aggregate = value
121
+
122
+ # use the assembly, instrument and data delivery methods to find the system of interest
123
+ node, sensor, stream = filter_urls(site, assembly, instrument, method)
124
+
125
+ # check the formatting of the start and end dates. We need to be able to parse and convert to an ISO format.
126
+ if start:
127
+ # check the formatting of the start date string and convert to the ISO format used by the M2M API
128
+ try:
129
+ start = parser.parse(start)
130
+ start = start.astimezone(pytz.utc)
131
+ start = start.strftime('%Y-%m-%dT%H:%M:%S.000Z')
132
+ except parser.ParserError:
133
+ raise SyntaxError('Formatting of the starting date string needs to be in a recognizable format')
134
+
135
+ if stop:
136
+ # check the formatting of the stop date string and convert to the ISO format used by the M2M API
137
+ try:
138
+ stop = parser.parse(stop)
139
+ stop = stop.astimezone(pytz.utc)
140
+ stop = stop.strftime('%Y-%m-%dT%H:%M:%S.000Z')
141
+ except parser.ParserError:
142
+ raise SyntaxError('Formatting of the ending date string needs to be in a recognizable format')
143
+
144
+ if deploy:
145
+ # Determine start and end dates based on the deployment number
146
+ start, stop = get_deployment_dates(site, node[0], sensor[0], deploy)
147
+ if not start or not stop:
148
+ exit_text = ('Deployment dates are unavailable for %s-%s-%s-%s, deployment %02d.' % (site.lower(), assembly,
149
+ instrument, method,
150
+ deploy))
151
+ raise RuntimeWarning(exit_text)
152
+
153
+ # for some cases, there maybe more than 1 stream, but in general, we only want the first one
154
+ if isinstance(stream[0], list):
155
+ stream = stream[0][0]
156
+ else:
157
+ stream = stream[0]
158
+
159
+ tag = ('.*{instrument}.*\\.nc$'.format(instrument=instrument.upper())) # set regex tag to use when downloading
160
+ data = None # setup the default data set
161
+
162
+ # check if there are multiple instances of this instrument class on the assembly
163
+ if len(node) > 1:
164
+ print('There are multiple instances of the instrument %s under %s-%s.' % (instrument, site.lower(), assembly))
165
+
166
+ # check if we are aggregating the multiple instruments into a single data set
167
+ if isinstance(aggregate, int):
168
+ if aggregate == 0:
169
+ # request all of the instruments associated with this site, assembly, instrument and method
170
+ print(('Requesting all %d instances of this instrument. Data sets will be concatenated\n'
171
+ 'and a new variable called `sensor_count` will be added to help distinguish the \n'
172
+ 'instruments for later processing.') % len(node))
173
+ for i in range(len(node)):
174
+ r = m2m_request(site, node[i], sensor[i], method, stream, start, stop)
175
+ if r:
176
+ temp = m2m_collect(r, tag)
177
+ temp['sensor_count'] = temp['deployment'] * 0 + i + 1
178
+ if not data:
179
+ data = temp
180
+ else:
181
+ data = xr.concat([data, temp], dim='time')
182
+ else:
183
+ # request a specific instrument of the multiple instruments associated with this site, assembly,
184
+ # instrument and method.
185
+ if aggregate > len(node):
186
+ raise SyntaxError('Only %d instruments available, you selected %d' % (len(node), aggregate))
187
+
188
+ print('Requesting instrument %d out of %d.' % (aggregate, len(node)))
189
+ i = aggregate - 1
190
+ r = m2m_request(site, node[i], sensor[i], method, stream, start, stop)
191
+ if r:
192
+ data = m2m_collect(r, tag)
193
+ else:
194
+ r = m2m_request(site, node[0], sensor[0], method, stream, start, stop)
195
+ if r:
196
+ data = m2m_collect(r, tag)
197
+
198
+ if not data:
199
+ raise RuntimeWarning('Data unavailable for %s-%s-%s-%s.' % (site.lower(), assembly, instrument, method))
200
+
201
+ # return the resulting data, which is an xarray.Dataset object
202
+ for v in data.variables:
203
+ # first convert strings with data types set as objects or S64 with binary encoding
204
+ if data[v].dtype == np.dtype('O') or data[v].dtype == np.dtype('S64'):
205
+ data[v] = data[v].astype(np.str)
206
+
207
+ return data
208
+
209
+
210
+ def main(argv=None):
211
+ """
212
+ CLI interface to the data_request function. Requests data via the OOI M2M
213
+ API using the site code, assembly type, instrument class and data delivery
214
+ method as defined in the m2m_urls.yml to construct the OOI specific data
215
+ request.
216
+
217
+ :param argv: Command line inputs used by the function are:
218
+
219
+ site (-s, --site): OOI site code as an 8 character string (required)
220
+ assembly (-a, --assembly): Assembly or subassembly type where the
221
+ instrument is located (required)
222
+ instrument (-i, --instrument): OOI instrument class name for the
223
+ instrument of interest (required)
224
+ method (-m, --method): The data delivery method for the system of
225
+ interest (required)
226
+ outfile (-o, --outfile): An absolute or relative path and file name
227
+ for where the resulting data should be saved as a NetCDF file
228
+ (required)
229
+
230
+ start (-bt, --beginDT): Starting or beginning date/time for the data
231
+ request in a dateutil.parser recognizable form. If ``None``, the
232
+ default, the beginning of the data record will be used (optional)
233
+ stop (-et, --endDT): Ending date/time for the data request in a
234
+ dateutil.parser recognizable form If ``None``, the default, the end
235
+ date of the data record will be used (optional)
236
+ deploy (-dp, --deploy): Use the deployment number, entered as an
237
+ integer, to set the beginning and ending dates If ``None``, the
238
+ default, the beginning and ending dates are used. If you enter
239
+ both, the deployment number will take priority in setting the start
240
+ and end dates (optional)
241
+ aggregate (-ag, --aggregate): In cases where more than one instance of
242
+ an instrument class is part of an assembly, collect all of the data
243
+ if the integer value entered is ``0``, or the specific instance of
244
+ the instrument is requested if any value greater than ``0`` is
245
+ used. If ``None``, the default, the first instance of an instrument
246
+ will be requested. (optional)
247
+
248
+ :return: Saves the data to disk as a NetCDF file.
249
+ """
250
+ args = dr_inputs(argv)
251
+ site = args.site
252
+ assembly = args.assembly
253
+ instrument = args.instrument
254
+ method = args.method
255
+ outfile = os.path.abspath(args.outfile)
256
+ deploy = args.deploy
257
+ start = args.start
258
+ stop = args.stop
259
+ aggregate = args.aggregate
260
+
261
+ # request the data
262
+ data = data_request(site, assembly, instrument, method, start=start, stop=stop, deploy=deploy, aggregate=aggregate)
263
+
264
+ # save the data to disk
265
+ if not os.path.exists(os.path.dirname(outfile)):
266
+ os.makedirs(os.path.dirname(outfile))
267
+
268
+ data.to_netcdf(outfile, mode='w', format='NETCDF4', engine='h5netcdf')
269
+
270
+
271
+ if __name__ == '__main__':
272
+ main()