pypromice 1.4.2__tar.gz → 1.4.4__tar.gz

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.

Potentially problematic release.


This version of pypromice might be problematic. Click here for more details.

Files changed (60) hide show
  1. {pypromice-1.4.2/src/pypromice.egg-info → pypromice-1.4.4}/PKG-INFO +1 -1
  2. {pypromice-1.4.2 → pypromice-1.4.4}/setup.py +1 -1
  3. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/bufr_utilities.py +2 -1
  4. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/L1toL2.py +30 -24
  5. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/value_clipping.py +15 -12
  6. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/resources/variables.csv +32 -32
  7. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/tx/tx.py +4 -4
  8. pypromice-1.4.4/src/pypromice/utilities/dependency_graph.py +101 -0
  9. {pypromice-1.4.2 → pypromice-1.4.4/src/pypromice.egg-info}/PKG-INFO +1 -1
  10. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice.egg-info/SOURCES.txt +1 -1
  11. pypromice-1.4.2/src/pypromice/tx/get_watsontx.py +0 -147
  12. {pypromice-1.4.2 → pypromice-1.4.4}/LICENSE.txt +0 -0
  13. {pypromice-1.4.2 → pypromice-1.4.4}/MANIFEST.in +0 -0
  14. {pypromice-1.4.2 → pypromice-1.4.4}/README.md +0 -0
  15. {pypromice-1.4.2 → pypromice-1.4.4}/setup.cfg +0 -0
  16. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/__init__.py +0 -0
  17. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/get/__init__.py +0 -0
  18. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/get/get.py +0 -0
  19. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/get/get_promice_data.py +0 -0
  20. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/__init__.py +0 -0
  21. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/bufr_to_csv.py +0 -0
  22. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/create_bufr_files.py +0 -0
  23. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/get_bufr.py +0 -0
  24. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/make_metadata_csv.py +0 -0
  25. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/positions_seed.csv +0 -0
  26. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/postprocess/real_time_utilities.py +0 -0
  27. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/L0toL1.py +0 -0
  28. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/L2toL3.py +0 -0
  29. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/__init__.py +0 -0
  30. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/aws.py +0 -0
  31. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/get_l2.py +0 -0
  32. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/get_l2tol3.py +0 -0
  33. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/join_l2.py +0 -0
  34. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/join_l3.py +0 -0
  35. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/load.py +0 -0
  36. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/resample.py +0 -0
  37. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/utilities.py +0 -0
  38. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/process/write.py +0 -0
  39. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/__init__.py +0 -0
  40. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/github_data_issues.py +0 -0
  41. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/percentiles/__init__.py +0 -0
  42. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/percentiles/compute_thresholds.py +0 -0
  43. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/percentiles/outlier_detector.py +0 -0
  44. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/percentiles/thresholds.csv +0 -0
  45. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/qc/persistence.py +0 -0
  46. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/resources/__init__.py +0 -0
  47. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/resources/file_attributes.csv +0 -0
  48. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/resources/variable_aliases_GC-Net.csv +0 -0
  49. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/station_configuration.py +0 -0
  50. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/tx/__init__.py +0 -0
  51. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/tx/get_l0tx.py +0 -0
  52. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/tx/get_msg.py +0 -0
  53. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/tx/payload_formats.csv +0 -0
  54. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/tx/payload_types.csv +0 -0
  55. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/utilities/__init__.py +0 -0
  56. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice/utilities/git.py +0 -0
  57. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice.egg-info/dependency_links.txt +0 -0
  58. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice.egg-info/entry_points.txt +0 -0
  59. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice.egg-info/requires.txt +0 -0
  60. {pypromice-1.4.2 → pypromice-1.4.4}/src/pypromice.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypromice
3
- Version: 1.4.2
3
+ Version: 1.4.4
4
4
  Summary: PROMICE/GC-Net data processing toolbox
5
5
  Home-page: https://github.com/GEUS-Glaciology-and-Climate/pypromice
6
6
  Author: GEUS Glaciology and Climate
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="pypromice",
8
- version="1.4.2",
8
+ version="1.4.4",
9
9
  author="GEUS Glaciology and Climate",
10
10
  description="PROMICE/GC-Net data processing toolbox",
11
11
  long_description=long_description,
@@ -120,7 +120,8 @@ class BUFRVariables:
120
120
  )
121
121
  # https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/012101
122
122
  # Scale: 2, unit: K
123
- airTemperature: float = attrs.field(converter=round_converter(2))
123
+ # NOTE: The expected scale is 2, but our instantanous data is rounded to 1 decimal.
124
+ airTemperature: float = attrs.field(converter=round_converter(1))
124
125
  # There is also a Dewpoint temperature in this template: 012103 which is currently unused.
125
126
  # https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/012103
126
127
  # Scale: 0, unit: %
@@ -94,7 +94,7 @@ def toL2(
94
94
  .ffill().bfill())
95
95
  mask = (np.abs(ds.gps_alt - baseline_elevation) < 100) & ds.gps_alt.notnull()
96
96
  ds[['gps_alt','gps_lon', 'gps_lat']] = ds[['gps_alt','gps_lon', 'gps_lat']].where(mask)
97
-
97
+
98
98
  # removing dlr and ulr that are missing t_rad
99
99
  # this is done now becasue t_rad can be filtered either manually or with persistence
100
100
  ds['dlr'] = ds.dlr.where(ds.t_rad.notnull())
@@ -113,12 +113,12 @@ def toL2(
113
113
  if ~ds['t_i'].isnull().all():
114
114
  ds['rh_i_cor'] = correctHumidity(ds['rh_i'], ds['t_i'],
115
115
  T_0, T_100, ews, ei0)
116
-
116
+
117
117
  # Determiune cloud cover for on-ice stations
118
118
  cc = calcCloudCoverage(ds['t_u'], T_0, eps_overcast, eps_clear, # Calculate cloud coverage
119
119
  ds['dlr'], ds.attrs['station_id'])
120
120
  ds['cc'] = (('time'), cc.data)
121
-
121
+
122
122
  # Determine surface temperature
123
123
  ds['t_surf'] = calcSurfaceTemperature(T_0, ds['ulr'], ds['dlr'], # Calculate surface temperature
124
124
  emissivity)
@@ -136,7 +136,7 @@ def toL2(
136
136
  else:
137
137
  lat = ds['gps_lat'].mean()
138
138
  lon = ds['gps_lon'].mean()
139
-
139
+
140
140
  # smoothing tilt and rot
141
141
  ds['tilt_x'] = smoothTilt(ds['tilt_x'])
142
142
  ds['tilt_y'] = smoothTilt(ds['tilt_y'])
@@ -151,8 +151,8 @@ def toL2(
151
151
  ZenithAngle_rad, ZenithAngle_deg = calcZenith(lat, Declination_rad, # Calculate zenith
152
152
  HourAngle_rad, deg2rad,
153
153
  rad2deg)
154
-
155
-
154
+
155
+
156
156
  # Correct Downwelling shortwave radiation
157
157
  DifFrac = 0.2 + 0.8 * cc
158
158
  CorFac_all = calcCorrectionFactor(Declination_rad, phi_sensor_rad, # Calculate correction
@@ -186,9 +186,9 @@ def toL2(
186
186
  TOA_crit_nopass = (ds['dsr_cor'] > (0.9 * isr_toa + 10)) # Determine filter
187
187
  ds['dsr_cor'][TOA_crit_nopass] = np.nan # Apply filter and interpolate
188
188
  ds['usr_cor'][TOA_crit_nopass] = np.nan
189
-
190
- ds['dsr_cor'] = ds.dsr_cor.where(ds.dsr.notnull())
191
- ds['usr_cor'] = ds.usr_cor.where(ds.usr.notnull())
189
+
190
+ ds['dsr_cor'] = ds.dsr_cor.where(ds.dsr.notnull())
191
+ ds['usr_cor'] = ds.usr_cor.where(ds.usr.notnull())
192
192
  # # Check sun position
193
193
  # sundown = ZenithAngle_deg >= 90
194
194
  # _checkSunPos(ds, OKalbedos, sundown, sunonlowerdome, TOA_crit_nopass)
@@ -205,27 +205,33 @@ def toL2(
205
205
  ds['precip_l_cor'], ds['precip_l_rate']= correctPrecip(ds['precip_l'],
206
206
  ds['wspd_l'])
207
207
 
208
- # Get directional wind speed
209
- ds['wdir_u'] = ds['wdir_u'].where(ds['wspd_u'] != 0)
210
- ds['wspd_x_u'], ds['wspd_y_u'] = calcDirWindSpeeds(ds['wspd_u'], ds['wdir_u'])
208
+ get_directional_wind_speed(ds) # Get directional wind speed
209
+
210
+ ds = clip_values(ds, vars_df)
211
+ return ds
212
+
213
+ def get_directional_wind_speed(ds: xr.Dataset) -> xr.Dataset:
214
+ """
215
+ Calculate directional wind speed from wind speed and direction and mutates the dataset
216
+ """
217
+
218
+ ds['wdir_u'] = ds['wdir_u'].where(ds['wspd_u'] != 0)
219
+ ds['wspd_x_u'], ds['wspd_y_u'] = calcDirWindSpeeds(ds['wspd_u'], ds['wdir_u'])
211
220
 
212
- if ds.attrs['number_of_booms']==2:
213
- ds['wdir_l'] = ds['wdir_l'].where(ds['wspd_l'] != 0)
221
+ if ds.attrs['number_of_booms']==2:
222
+ ds['wdir_l'] = ds['wdir_l'].where(ds['wspd_l'] != 0)
214
223
  ds['wspd_x_l'], ds['wspd_y_l'] = calcDirWindSpeeds(ds['wspd_l'], ds['wdir_l'])
215
224
 
216
225
  if hasattr(ds, 'wdir_i'):
217
226
  if ~ds['wdir_i'].isnull().all() and ~ds['wspd_i'].isnull().all():
218
- ds['wdir_i'] = ds['wdir_i'].where(ds['wspd_i'] != 0)
219
- ds['wspd_x_i'], ds['wspd_y_i'] = calcDirWindSpeeds(ds['wspd_i'], ds['wdir_i'])
220
-
221
-
222
- ds = clip_values(ds, vars_df)
227
+ ds['wdir_i'] = ds['wdir_i'].where(ds['wspd_i'] != 0)
228
+ ds['wspd_x_i'], ds['wspd_y_i'] = calcDirWindSpeeds(ds['wspd_i'], ds['wdir_i'])
223
229
  return ds
224
230
 
225
231
 
226
232
  def calcDirWindSpeeds(wspd, wdir, deg2rad=np.pi/180):
227
233
  '''Calculate directional wind speed from wind speed and direction
228
-
234
+
229
235
  Parameters
230
236
  ----------
231
237
  wspd : xr.Dataarray
@@ -234,16 +240,16 @@ def calcDirWindSpeeds(wspd, wdir, deg2rad=np.pi/180):
234
240
  Wind direction data array
235
241
  deg2rad : float
236
242
  Degree to radians coefficient. The default is np.pi/180
237
-
243
+
238
244
  Returns
239
245
  -------
240
246
  wspd_x : xr.Dataarray
241
247
  Wind speed in X direction
242
248
  wspd_y : xr.Datarray
243
249
  Wind speed in Y direction
244
- '''
250
+ '''
245
251
  wspd_x = wspd * np.sin(wdir * deg2rad)
246
- wspd_y = wspd * np.cos(wdir * deg2rad)
252
+ wspd_y = wspd * np.cos(wdir * deg2rad)
247
253
  return wspd_x, wspd_y
248
254
 
249
255
 
@@ -328,7 +334,7 @@ def smoothTilt(da: xr.DataArray, threshold=0.2):
328
334
  either X or Y smoothed tilt inclinometer measurements
329
335
  '''
330
336
  # we calculate the moving standard deviation over a 3-day sliding window
331
- # hourly resampling is necessary to make sure the same threshold can be used
337
+ # hourly resampling is necessary to make sure the same threshold can be used
332
338
  # for 10 min and hourly data
333
339
  moving_std_gap_filled = da.to_series().resample('h').median().rolling(
334
340
  3*24, center=True, min_periods=2
@@ -2,6 +2,8 @@ import numpy as np
2
2
  import pandas
3
3
  import xarray
4
4
 
5
+ from pypromice.utilities.dependency_graph import DependencyGraph
6
+
5
7
 
6
8
  def clip_values(
7
9
  ds: xarray.Dataset,
@@ -27,9 +29,15 @@ def clip_values(
27
29
  cols = ["lo", "hi", "OOL"]
28
30
  assert set(cols) <= set(var_configurations.columns)
29
31
 
30
- variable_limits = var_configurations[cols].dropna(how="all")
31
- for var, row in variable_limits.iterrows():
32
+ variable_limits = var_configurations[cols].assign(
33
+ dependents=lambda df: df.OOL.fillna("").str.split(),
34
+ # Find the closure of dependents using the DependencyGraph class
35
+ dependents_closure=lambda df: DependencyGraph.from_child_mapping(
36
+ df.dependents
37
+ ).child_closure_mapping(),
38
+ )
32
39
 
40
+ for var, row in variable_limits.iterrows():
33
41
  if var not in list(ds.variables):
34
42
  continue
35
43
 
@@ -38,15 +46,10 @@ def clip_values(
38
46
  if ~np.isnan(row.hi):
39
47
  ds[var] = ds[var].where(ds[var] <= row.hi)
40
48
 
41
- other_vars = row.OOL
42
- if isinstance(other_vars, str) and ~ds[var].isnull().all():
43
- for o in other_vars.split():
44
- if o not in list(ds.variables):
45
- continue
46
- else:
47
- if ~np.isnan(row.lo):
48
- ds[var] = ds[var].where(ds[var] >= row.lo)
49
- if ~np.isnan(row.hi):
50
- ds[var] = ds[var].where(ds[var] <= row.hi)
49
+ # Flag dependents as NaN if parent is NaN
50
+ for o in row.dependents_closure:
51
+ if o not in list(ds.variables):
52
+ continue
53
+ ds[o] = ds[o].where(ds[var].notnull())
51
54
 
52
55
  return ds
@@ -1,53 +1,53 @@
1
1
  field,standard_name,long_name,units,coverage_content_type,coordinates,instantaneous_hourly,where_to_find,lo,hi,OOL,station_type,L0,L2,L3,max_decimals
2
2
  time,time,Time,yyyy-mm-dd HH:MM:SS,physicalMeasurement,time,,,,,,all,1,1,1,
3
3
  rec,record,Record,-,referenceInformation,time,,L0 or L2,,,,all,1,1,0,0
4
- p_u,air_pressure,Air pressure (upper boom),hPa,physicalMeasurement,time,FALSE,,650,1100,z_pt z_pt_cor dshf_u dlhf_u qh_u,all,1,1,1,4
5
- p_l,air_pressure,Air pressure (lower boom),hPa,physicalMeasurement,time,FALSE,,650,1100,dshf_l dlhf_l qh_l,two-boom,1,1,1,4
6
- t_u,air_temperature,Air temperature (upper boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,rh_u_cor cc dsr_cor usr_cor z_boom z_stake dshf_u dlhf_u qh_u,all,1,1,1,4
7
- t_l,air_temperature,Air temperature (lower boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,rh_l_cor z_boom_l dshf_l dlhf_l qh_l,two-boom,1,1,1,4
8
- rh_u,relative_humidity,Relative humidity (upper boom),%,physicalMeasurement,time,FALSE,,0,100,rh_u_cor,all,1,1,1,4
9
- rh_u_cor,relative_humidity_corrected,Relative humidity (upper boom) - corrected,%,modelResult,time,FALSE,L2 or later,0,150,dshf_u dlhf_u qh_u,all,0,1,1,4
10
- qh_u,specific_humidity,Specific humidity (upper boom),kg/kg,modelResult,time,FALSE,L2 or later,0,100,,all,0,1,1,4
11
- rh_l,relative_humidity,Relative humidity (lower boom),%,physicalMeasurement,time,FALSE,,0,100,rh_l_cor,two-boom,1,1,1,4
12
- rh_l_cor,relative_humidity_corrected,Relative humidity (lower boom) - corrected,%,modelResult,time,FALSE,L2 or later,0,150,dshf_l dlhf_l qh_l,two-boom,0,1,1,4
4
+ p_u,air_pressure,Air pressure (upper boom),hPa,physicalMeasurement,time,FALSE,,650,1100,"",all,1,1,1,4
5
+ p_l,air_pressure,Air pressure (lower boom),hPa,physicalMeasurement,time,FALSE,,650,1100,"",two-boom,1,1,1,4
6
+ t_u,air_temperature,Air temperature (upper boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,"",all,1,1,1,4
7
+ t_l,air_temperature,Air temperature (lower boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,"",two-boom,1,1,1,4
8
+ rh_u,relative_humidity,Relative humidity (upper boom),%,physicalMeasurement,time,FALSE,,0,100,"",all,1,1,1,4
9
+ rh_u_cor,relative_humidity_corrected,Relative humidity (upper boom) - corrected,%,modelResult,time,FALSE,L2 or later,0,150,"",all,0,1,1,4
10
+ qh_u,specific_humidity,Specific humidity (upper boom),kg/kg,modelResult,time,FALSE,L2 or later,0,100,"",all,0,1,1,4
11
+ rh_l,relative_humidity,Relative humidity (lower boom),%,physicalMeasurement,time,FALSE,,0,100,"",two-boom,1,1,1,4
12
+ rh_l_cor,relative_humidity_corrected,Relative humidity (lower boom) - corrected,%,modelResult,time,FALSE,L2 or later,0,150,"",two-boom,0,1,1,4
13
13
  qh_l,specific_humidity,Specific humidity (lower boom),kg/kg,modelResult,time,FALSE,L2 or later,0,100,,two-boom,0,1,1,4
14
- wspd_u,wind_speed,Wind speed (upper boom),m s-1,physicalMeasurement,time,FALSE,,0,100,"wdir_u wspd_x_u wspd_y_u dshf_u dlhf_u qh_u, precip_u",all,1,1,1,4
15
- wspd_l,wind_speed,Wind speed (lower boom),m s-1,physicalMeasurement,time,FALSE,,0,100,"wdir_l wspd_x_l wspd_y_l dshf_l dlhf_l qh_l , precip_l",two-boom,1,1,1,4
14
+ wspd_u,wind_speed,Wind speed (upper boom),m s-1,physicalMeasurement,time,FALSE,,0,100,wdir_u wspd_x_u wspd_y_u,all,1,1,1,4
15
+ wspd_l,wind_speed,Wind speed (lower boom),m s-1,physicalMeasurement,time,FALSE,,0,100,wdir_l wspd_x_l wspd_y_l,two-boom,1,1,1,4
16
16
  wdir_u,wind_from_direction,Wind from direction (upper boom),degrees,physicalMeasurement,time,FALSE,,1,360,wspd_x_u wspd_y_u,all,1,1,1,4
17
17
  wdir_std_u,wind_from_direction_standard_deviation,Wind from direction (standard deviation),degrees,qualityInformation,time,FALSE,L0 or L2,,,,one-boom,1,1,0,4
18
18
  wdir_l,wind_from_direction,Wind from direction (lower boom),degrees,physicalMeasurement,time,FALSE,,1,360,wspd_x_l wspd_y_l,two-boom,1,1,1,4
19
- wspd_x_u,wind_speed_from_x_direction,Wind speed from x direction (upper boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,wdir_u wspd_u,all,0,1,1,4
20
- wspd_y_u,wind_speed_from_y_direction,Wind speed from y direction (upper boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,wdir_u wspd_u,all,0,1,1,4
21
- wspd_x_l,wind_speed_from_x_direction,Wind speed from x direction (lower boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,wdir_l wspd_l,two-boom,0,1,1,4
22
- wspd_y_l,wind_speed_from_y_direction,Wind speed from y direction (lower boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,wdir_l wspd_l,two-boom,0,1,1,4
23
- dsr,surface_downwelling_shortwave_flux,Downwelling shortwave radiation,W m-2,physicalMeasurement,time,FALSE,,-10,1500,albedo dsr_cor usr_cor,all,1,1,1,4
19
+ wspd_x_u,wind_speed_from_x_direction,Wind speed from x direction (upper boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,"",all,0,1,1,4
20
+ wspd_y_u,wind_speed_from_y_direction,Wind speed from y direction (upper boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,"",all,0,1,1,4
21
+ wspd_x_l,wind_speed_from_x_direction,Wind speed from x direction (lower boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,"",two-boom,0,1,1,4
22
+ wspd_y_l,wind_speed_from_y_direction,Wind speed from y direction (lower boom),m s-1,modelResult,time,FALSE,L0 or L2,-100,100,"",two-boom,0,1,1,4
23
+ dsr,surface_downwelling_shortwave_flux,Downwelling shortwave radiation,W m-2,physicalMeasurement,time,FALSE,,-10,1500,"",all,1,1,1,4
24
24
  dsr_cor,surface_downwelling_shortwave_flux_corrected,Downwelling shortwave radiation - corrected,W m-2,modelResult,time,FALSE,L2 or later,,,,all,0,1,1,4
25
- usr,surface_upwelling_shortwave_flux,Upwelling shortwave radiation,W m-2,physicalMeasurement,time,FALSE,,-10,1000,albedo dsr_cor usr_cor,all,1,1,1,4
25
+ usr,surface_upwelling_shortwave_flux,Upwelling shortwave radiation,W m-2,physicalMeasurement,time,FALSE,,-10,1000,"",all,1,1,1,4
26
26
  usr_cor,surface_upwelling_shortwave_flux_corrected,Upwelling shortwave radiation - corrected,W m-2,modelResult,time,FALSE,L2 or later,0,1000,,all,0,1,1,4
27
27
  albedo,surface_albedo,Albedo,-,modelResult,time,FALSE,L2 or later,,,,all,0,1,1,4
28
- dlr,surface_downwelling_longwave_flux,Downwelling longwave radiation,W m-2,physicalMeasurement,time,FALSE,,50,500,albedo dsr_cor usr_cor cc t_surf,all,1,1,1,4
29
- ulr,surface_upwelling_longwave_flux,Upwelling longwave radiation,W m-2,physicalMeasurement,time,FALSE,,50,500,t_surf,all,1,1,1,4
28
+ dlr,surface_downwelling_longwave_flux,Downwelling longwave radiation,W m-2,physicalMeasurement,time,FALSE,,50,500,"",all,1,1,1,4
29
+ ulr,surface_upwelling_longwave_flux,Upwelling longwave radiation,W m-2,physicalMeasurement,time,FALSE,,50,500,"",all,1,1,1,4
30
30
  cc,cloud_area_fraction,Cloud cover,%,modelResult,time,FALSE,L2 or later,,,,all,0,1,1,4
31
- t_surf,surface_temperature,Surface temperature,C,modelResult,time,FALSE,L2 or later,-80,40,dshf_u dlhf_u qh_u,all,0,1,1,4
31
+ t_surf,surface_temperature,Surface temperature,C,modelResult,time,FALSE,L2 or later,-80,40,"",all,0,1,1,4
32
32
  dlhf_u,surface_downward_latent_heat_flux,Latent heat flux (upper boom),W m-2,modelResult,time,FALSE,L3 or later,,,,all,0,0,1,4
33
33
  dlhf_l,surface_downward_latent_heat_flux,Latent heat flux (lower boom),W m-2,modelResult,time,FALSE,L3 or later,,,,two-boom,0,0,1,4
34
34
  dshf_u,surface_downward_sensible_heat_flux,Sensible heat flux (upper boom),W m-2,modelResult,time,FALSE,L3 or later,,,,all,0,0,1,4
35
35
  dshf_l,surface_downward_sensible_heat_flux,Sensible heat flux (lower boom),W m-2,modelResult,time,FALSE,L3 or later,,,,two-boom,0,0,1,4
36
- z_boom_u,distance_to_surface_from_boom,Upper boom height,m,physicalMeasurement,time,TRUE,,0.3,10,dshf_u dlhf_u qh_u,all,1,1,1,4
36
+ z_boom_u,distance_to_surface_from_boom,Upper boom height,m,physicalMeasurement,time,TRUE,,0.3,10,"",all,1,1,1,4
37
37
  z_boom_q_u,distance_to_surface_from_boom_quality,Upper boom height (quality),-,qualityInformation,time,TRUE,L0 or L2,,,,all,1,1,0,4
38
- z_boom_l,distance_to_surface_from_boom,Lower boom height,m,physicalMeasurement,time,TRUE,,0.3,5,dshf_l dlhf_l qh_l,two-boom,1,1,1,4
38
+ z_boom_l,distance_to_surface_from_boom,Lower boom height,m,physicalMeasurement,time,TRUE,,0.3,5,"",two-boom,1,1,1,4
39
39
  z_boom_q_l,distance_to_surface_from_boom_quality,Lower boom height (quality),-,qualityInformation,time,TRUE,L0 or L2,,,,two-boom,1,1,0,4
40
40
  z_stake,distance_to_surface_from_stake_assembly,Stake height,m,physicalMeasurement,time,TRUE,,0.3,8,,one-boom,1,1,1,4
41
41
  z_stake_q,distance_to_surface_from_stake_assembly_quality,Stake height (quality),-,qualityInformation,time,TRUE,L0 or L2,,,,one-boom,1,1,0,4
42
- z_pt,depth_of_pressure_transducer_in_ice,Depth of pressure transducer in ice,m,physicalMeasurement,time,FALSE,,0,30,z_pt_cor,one-boom,1,1,1,4
42
+ z_pt,depth_of_pressure_transducer_in_ice,Depth of pressure transducer in ice,m,physicalMeasurement,time,FALSE,,0,30,"",one-boom,1,1,1,4
43
43
  z_pt_cor,depth_of_pressure_transducer_in_ice_corrected,Depth of pressure transducer in ice - corrected,m,modelResult,time,FALSE,L2 or later,0,30,,one-boom,0,1,1,4
44
44
  z_surf_combined,height_of_surface_combined,"Surface height combined from multiple sensors, relative to ice surface height at installation",m,modelResult,time,FALSE,L3,,,,all,0,0,1,4
45
45
  z_ice_surf,height_of_ice_surface,"Ice surface height, relative to ice surface height at installation and calculated from pt_cor and z_stake",m,modelResult,time,FALSE,L3,,,,one-boom,0,0,1,4
46
46
  snow_height,height_of_snow,"Snow surface height, relative to ice surface",m,modelResult,time,FALSE,L3,0,,,one-boom,0,0,1,4
47
- precip_u,precipitation,Precipitation (upper boom) (cumulative solid & liquid),mm,physicalMeasurement,time,TRUE,,0,,precip_u_cor precip_u_rate,all,1,1,1,4
47
+ precip_u,precipitation,Precipitation (upper boom) (cumulative solid & liquid),mm,physicalMeasurement,time,TRUE,,0,,"",all,1,1,1,4
48
48
  precip_u_cor,precipitation_corrected,Precipitation (upper boom) (cumulative solid & liquid) – corrected,mm,modelResult,time,TRUE,L2 or later,0,,,all,0,1,1,4
49
49
  precip_u_rate,precipitation_rate,Precipitation rate (upper boom) (cumulative solid & liquid) – corrected,mm,modelResult,time,TRUE,L2 or later,0,,,all,0,1,1,4
50
- precip_l,precipitation,Precipitation (lower boom) (cumulative solid & liquid),mm,physicalMeasurement,time,TRUE,,0,,precip_l_cor precip_l_rate,two-boom,1,1,1,4
50
+ precip_l,precipitation,Precipitation (lower boom) (cumulative solid & liquid),mm,physicalMeasurement,time,TRUE,,0,,"",two-boom,1,1,1,4
51
51
  precip_l_cor,precipitation_corrected,Precipitation (lower boom) (cumulative solid & liquid) – corrected,mm,modelResult,time,TRUE,L2 or later,0,,,two-boom,0,1,1,4
52
52
  precip_l_rate,precipitation_rate,Precipitation rate (lower boom) (cumulative solid & liquid) – corrected,mm,modelResult,time,TRUE,L2 or later,0,,,two-boom,0,1,1,4
53
53
  t_i_1,ice_temperature_at_t1,Ice temperature at sensor 1,degrees_C,physicalMeasurement,time,FALSE,,-80,1,,all,1,1,1,4
@@ -73,8 +73,8 @@ d_t_i_9,depth_of_thermistor_9,Depth of thermistor 9,m,modelResult,time,FALSE,L3,
73
73
  d_t_i_10,depth_of_thermistor_10,Depth of thermistor 10,m,modelResult,time,FALSE,L3,-10,100,,two-boom,0,0,1,4
74
74
  d_t_i_11,depth_of_thermistor_11,Depth of thermistor 11,m,modelResult,time,FALSE,L3,-10,100,,two-boom,0,0,1,4
75
75
  t_i_10m,10m_subsurface_temperature,10 m subsurface temperature,degrees_C,modelResult,time,FALSE,L3,-70,0,,all,0,0,1,4
76
- tilt_x,platform_view_angle_x,Tilt to east,degrees,physicalMeasurement,time,FALSE,,-30,30,dsr_cor usr_cor albedo,all,1,1,1,4
77
- tilt_y,platform_view_angle_y,Tilt to north,degrees,physicalMeasurement,time,FALSE,,-30,30,dsr_cor usr_cor albedo,all,1,1,1,4
76
+ tilt_x,platform_view_angle_x,Tilt to east,degrees,physicalMeasurement,time,FALSE,,-30,30,"",all,1,1,1,4
77
+ tilt_y,platform_view_angle_y,Tilt to north,degrees,physicalMeasurement,time,FALSE,,-30,30,"",all,1,1,1,4
78
78
  rot,platform_azimuth_angle,Station rotation from true North,degrees,physicalMeasurement,time,FALSE,,0,360,,all,1,1,1,2
79
79
  gps_lat,gps_latitude,Latitude,degrees_north,physicalMeasurement,time,TRUE,,50,83,,all,1,1,1,6
80
80
  gps_lon,gps_longitude,Longitude,degrees_east,physicalMeasurement,time,TRUE,,5,70,,all,1,1,1,6
@@ -93,14 +93,14 @@ batt_v_ini,,,-,physicalMeasurement,time,TRUE,L0 or L2,0,30,,,1,1,0,2
93
93
  batt_v_ss,battery_voltage_at_sample_start,Battery voltage (sample start),V,physicalMeasurement,time,TRUE,L0 or L2,0,30,,,1,1,0,2
94
94
  fan_dc_u,fan_current,Fan current (upper boom),mA,physicalMeasurement,time,TRUE,L0 or L2,0,200,,all,1,1,0,2
95
95
  fan_dc_l,fan_current,Fan current (lower boom),mA,physicalMeasurement,time,TRUE,,0,200,,two-boom,1,1,0,2
96
- freq_vw,frequency_of_precipitation_wire_vibration,Frequency of vibrating wire in precipitation gauge,Hz,physicalMeasurement,time,TRUE,L0 or L2,0,10000,precip_u,,1,1,0,
96
+ freq_vw,frequency_of_precipitation_wire_vibration,Frequency of vibrating wire in precipitation gauge,Hz,physicalMeasurement,time,TRUE,L0 or L2,0,10000,"",,1,1,0,
97
97
  t_log,temperature_of_logger,Logger temperature,degrees_C,physicalMeasurement,time,TRUE,,-80,40,,one-boom,1,1,0,4
98
- t_rad,temperature_of_radiation_sensor,Radiation sensor temperature,degrees_C,physicalMeasurement,time,FALSE,,-80,40,t_surf dlr ulr,all,1,1,1,4
98
+ t_rad,temperature_of_radiation_sensor,Radiation sensor temperature,degrees_C,physicalMeasurement,time,FALSE,,-80,40,"",all,1,1,1,4
99
99
  p_i,air_pressure,Air pressure (instantaneous) minus 1000,hPa,physicalMeasurement,time,TRUE,,-350,100,,all,1,1,1,4
100
100
  t_i,air_temperature,Air temperature (instantaneous),degrees_C,physicalMeasurement,time,TRUE,,-80,40,,all,1,1,1,4
101
- rh_i,relative_humidity,Relative humidity (instantaneous),%,physicalMeasurement,time,TRUE,,0,150,rh_i_cor,all,1,1,1,4
101
+ rh_i,relative_humidity,Relative humidity (instantaneous),%,physicalMeasurement,time,TRUE,,0,150,"",all,1,1,1,4
102
102
  rh_i_cor,relative_humidity_corrected,Relative humidity (instantaneous) – corrected,%,modelResult,time,TRUE,L2 or later,0,100,,all,0,1,1,4
103
103
  wspd_i,wind_speed,Wind speed (instantaneous),m s-1,physicalMeasurement,time,TRUE,,0,100,wdir_i wspd_x_i wspd_y_i,all,1,1,1,4
104
104
  wdir_i,wind_from_direction,Wind from direction (instantaneous),degrees,physicalMeasurement,time,TRUE,,1,360,wspd_x_i wspd_y_i,all,1,1,1,4
105
- wspd_x_i,wind_speed_from_x_direction,Wind speed from x direction (instantaneous),m s-1,modelResult,time,TRUE,L2 or later,-100,100,wdir_i wspd_i,all,0,1,1,4
106
- wspd_y_i,wind_speed_from_y_direction,Wind speed from y direction (instantaneous),m s-1,modelResult,time,TRUE,L2 or later,-100,100,wdir_i wspd_i,all,0,1,1,4
105
+ wspd_x_i,wind_speed_from_x_direction,Wind speed from x direction (instantaneous),m s-1,modelResult,time,TRUE,L2 or later,-100,100,"",all,0,1,1,4
106
+ wspd_y_i,wind_speed_from_y_direction,Wind speed from y direction (instantaneous),m s-1,modelResult,time,TRUE,L2 or later,-100,100,"",all,0,1,1,4
@@ -894,10 +894,10 @@ def sortLines(in_file, out_file, replace_unsorted=True): #
894
894
  # out_f.write(headers)
895
895
  out_f.writelines(unique_lines)
896
896
 
897
- # Replace input file with new sorted file
898
- if replace_unsorted:
899
- os.remove(in_file)
900
- os.rename(out_file, in_file)
897
+ # Replace input file with new sorted file
898
+ if replace_unsorted:
899
+ os.remove(in_file)
900
+ os.rename(out_file, in_file)
901
901
 
902
902
  def addTail(in_file, out_dir, aws_name, header_names='', lines_limit=100):
903
903
  '''Generate tails file from L0tx file
@@ -0,0 +1,101 @@
1
+ from typing import Mapping, Set, MutableMapping, Optional
2
+
3
+ import attr
4
+
5
+ __all__ = [
6
+ "DependencyNode",
7
+ "DependencyGraph",
8
+ ]
9
+
10
+
11
+ @attr.define
12
+ class DependencyNode:
13
+ name: str = attr.field()
14
+ parents: Set["DependencyNode"] = attr.field(factory=set)
15
+ children: Set["DependencyNode"] = attr.field(factory=set)
16
+
17
+ def add_child(self, child: "DependencyNode"):
18
+ self.children.add(child)
19
+ child.parents.add(self)
20
+
21
+ def get_children_closure(self, closure: Optional[Set[str]] = None) -> Set[str]:
22
+ is_root = closure is None
23
+ if closure is None:
24
+ closure = set()
25
+ if self.name in closure:
26
+ return closure
27
+ closure.add(self.name)
28
+ for child in self.children:
29
+ closure |= child.get_children_closure(closure)
30
+
31
+ if is_root:
32
+ closure.remove(self.name)
33
+ return closure
34
+
35
+ def get_parents_closure(self, closure: Optional[Set[str]] = None) -> Set[str]:
36
+ is_root = closure is None
37
+ if closure is None:
38
+ closure = set()
39
+ if self.name in closure:
40
+ return closure
41
+ closure.add(self.name)
42
+ for parent in self.parents:
43
+ closure |= parent.get_parents_closure(closure)
44
+
45
+ if is_root:
46
+ closure.remove(self.name)
47
+ return closure
48
+
49
+ def __hash__(self):
50
+ return hash(self.name)
51
+
52
+
53
+ @attr.define
54
+ class DependencyGraph:
55
+ nodes: MutableMapping[str, DependencyNode] = attr.field(factory=dict)
56
+
57
+ def add_node(self, name: str) -> DependencyNode:
58
+ if name not in self.nodes:
59
+ self.nodes[name] = DependencyNode(name)
60
+ return self.nodes[name]
61
+
62
+ def add_edge(self, parent: str, child: str):
63
+ parent_node = self.add_node(parent)
64
+ child_node = self.add_node(child)
65
+ parent_node.add_child(child_node)
66
+
67
+ @classmethod
68
+ def from_child_mapping(cls, mapping: Mapping[str, Set[str]]) -> "DependencyGraph":
69
+ graph = cls()
70
+ for var, children in mapping.items():
71
+ graph.add_node(var)
72
+ for child in children:
73
+ graph.add_edge(var, child)
74
+ return graph
75
+
76
+ @classmethod
77
+ def from_parent_mapping(cls, mapping: Mapping[str, Set[str]]) -> "DependencyGraph":
78
+ graph = cls()
79
+ for var, parents in mapping.items():
80
+ graph.add_node(var)
81
+ for parent in parents:
82
+ graph.add_edge(parent, var)
83
+ return graph
84
+
85
+ def child_mapping(self) -> Mapping[str, Set[str]]:
86
+ return {
87
+ node.name: {child.name for child in node.children}
88
+ for node in self.nodes.values()
89
+ }
90
+
91
+ def child_closure_mapping(self) -> Mapping[str, Set[str]]:
92
+ return {node.name: node.get_children_closure() for node in self.nodes.values()}
93
+
94
+ def parent_mapping(self) -> Mapping[str, Set[str]]:
95
+ return {
96
+ node.name: {parent.name for parent in node.parents}
97
+ for node in self.nodes.values()
98
+ }
99
+
100
+ def parent_closure_mapping(self) -> Mapping[str, Set[str]]:
101
+ return {node.name: node.get_parents_closure() for node in self.nodes.values()}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypromice
3
- Version: 1.4.2
3
+ Version: 1.4.4
4
4
  Summary: PROMICE/GC-Net data processing toolbox
5
5
  Home-page: https://github.com/GEUS-Glaciology-and-Climate/pypromice
6
6
  Author: GEUS Glaciology and Climate
@@ -49,9 +49,9 @@ src/pypromice/resources/variables.csv
49
49
  src/pypromice/tx/__init__.py
50
50
  src/pypromice/tx/get_l0tx.py
51
51
  src/pypromice/tx/get_msg.py
52
- src/pypromice/tx/get_watsontx.py
53
52
  src/pypromice/tx/payload_formats.csv
54
53
  src/pypromice/tx/payload_types.csv
55
54
  src/pypromice/tx/tx.py
56
55
  src/pypromice/utilities/__init__.py
56
+ src/pypromice/utilities/dependency_graph.py
57
57
  src/pypromice/utilities/git.py
@@ -1,147 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Created on Fri Jul 22 16:20:09 2022
5
-
6
- Script to get L0tx transmission messages from the Watson River station using
7
- the tx module
8
-
9
- @author: Penelope How, pho@geus.dk
10
- """
11
- from argparse import ArgumentParser
12
-
13
- from configparser import ConfigParser
14
- import os, imaplib, email, re
15
- from glob import glob
16
- from datetime import datetime
17
-
18
- from pypromice.tx import getMail, L0tx, sortLines
19
-
20
-
21
- def parse_arguments_watson():
22
- parser = ArgumentParser(description="AWS L0 transmission fetcher for Watson River measurements")
23
- parser.add_argument('-a', '--account', default=None, type=str, required=True, help='Email account .ini file')
24
- parser.add_argument('-p', '--password', default=None, type=str, required=True, help='Email credentials .ini file')
25
- parser.add_argument('-o', '--outpath', default=None, type=str, required=False, help='Path where to write output (if given)')
26
- parser.add_argument('-f', '--formats', default=None, type=str, required=False, help='Path to Payload format .csv file')
27
- parser.add_argument('-t', '--types', default=None, type=str, required=False, help='Path to Payload type .csv file')
28
- parser.add_argument('-u', '--uid', default=None, type=str, required=True, help='Last AWS uid .ini file')
29
- args = parser.parse_args()
30
- return args
31
-
32
- #------------------------------------------------------------------------------
33
- def get_watsontx():
34
- """Executed from the command line"""
35
- args = parse_arguments_watson()
36
-
37
- # Set payload formatter paths
38
- formatter_file = args.formats
39
- type_file = args.types
40
-
41
- # Set credential paths
42
- accounts_file = args.account
43
- credentials_file = args.password
44
-
45
- # Set last aws uid path
46
- # last_uid = 1000000
47
- uid_file = args.uid
48
-
49
- # Set last aws uid path
50
- with open(uid_file, 'r') as last_uid_f:
51
- last_uid = int(last_uid_f.readline())
52
-
53
- # Set output file directory
54
- out_dir = args.outpath
55
- if not os.path.exists(out_dir):
56
- os.mkdir(out_dir)
57
-
58
- #------------------------------------------------------------------------------
59
-
60
- # Define accounts and credentials ini file paths
61
- accounts_ini = ConfigParser()
62
- accounts_ini.read_file(open(accounts_file))
63
- accounts_ini.read(credentials_file)
64
-
65
- # Get credentials
66
- account = accounts_ini.get('aws', 'account')
67
- server = accounts_ini.get('aws', 'server')
68
- port = accounts_ini.getint('aws', 'port')
69
- password = accounts_ini.get('aws', 'password')
70
- if not password:
71
- password = input('password for AWS email account: ')
72
- print('AWS data from server %s, account %s' %(server, account))
73
-
74
- #------------------------------------------------------------------------------
75
-
76
- # Log in to email server
77
- mail_server = imaplib.IMAP4_SSL(server, port)
78
- typ, accountDetails = mail_server.login(account, password)
79
- if typ != 'OK':
80
- print('Not able to sign in!')
81
- raise
82
-
83
- # Grab new emails
84
- result, data = mail_server.select(mailbox='"[Gmail]/All Mail"',
85
- readonly=True)
86
- print('mailbox contains %s messages' %data[0])
87
-
88
- #------------------------------------------------------------------------------
89
-
90
- # Get L0tx datalines from email transmissions
91
- for uid, mail in getMail(mail_server, last_uid=last_uid):
92
- message = email.message_from_string(mail)
93
- try:
94
- name = str(message.get_all('subject')[0])
95
- d = datetime.strptime(message.get_all('date')[0],
96
- '%a, %d %b %Y %H:%M:%S %z')
97
- except:
98
- name=None
99
- d=None
100
-
101
- if name and ('Watson' in name or 'GIOS' in name):
102
- print(f'Watson/GIOS station message, {d.strftime("%Y-%m-%d %H:%M:%S")}')
103
-
104
- l0 = L0tx(message, formatter_file, type_file,
105
- sender_name=['emailrelay@konectgds.com','sbdservice'])
106
-
107
- if l0.msg:
108
- content, attachment = l0.getEmailBody()
109
- attachment_name = str(attachment.get_filename())
110
- out_fn = re.sub(r'\d*\.dat$', '', attachment_name) + '.txt'
111
- out_path = os.sep.join((out_dir, out_fn))
112
-
113
- print(f'Writing to {out_fn}')
114
- print(l0.msg)
115
-
116
- with open(out_path, mode='a') as out_f:
117
- out_f.write(l0.msg + '\n')
118
-
119
- #------------------------------------------------------------------------------
120
-
121
- # Sort L0tx files and add tails
122
- for f in glob(out_dir+'/*.txt'):
123
-
124
- # Sort lines in L0tx file and remove duplicates
125
- in_dirn, in_fn = os.path.split(f)
126
- out_fn = 'sorted_' + in_fn
127
- out_pn = os.sep.join((in_dirn, out_fn))
128
- sortLines(f, out_pn)
129
-
130
- # Close mail server if open
131
- if 'mail_server' in locals():
132
- print(f'\nClosing {account}')
133
- mail_server.close()
134
- resp = mail_server.logout()
135
- assert resp[0].upper() == 'BYE'
136
-
137
- # Write last aws uid to ini file
138
- try:
139
- with open(uid_file, 'w') as last_uid_f:
140
- last_uid_f.write(uid)
141
- except:
142
- print(f'Could not write last uid {uid} to {uid_file}')
143
-
144
- print('Finished')
145
-
146
- if __name__ == "__main__":
147
- get_watsontx()
File without changes
File without changes
File without changes
File without changes