windborne 1.0.5__py3-none-any.whl → 1.0.7__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.
- windborne/__init__.py +11 -4
- windborne/cli.py +168 -75
- windborne/data_api.py +629 -119
- windborne/utils.py +40 -16
- {windborne-1.0.5.dist-info → windborne-1.0.7.dist-info}/METADATA +1 -1
- windborne-1.0.7.dist-info/RECORD +11 -0
- windborne-1.0.5.dist-info/RECORD +0 -11
- {windborne-1.0.5.dist-info → windborne-1.0.7.dist-info}/WHEEL +0 -0
- {windborne-1.0.5.dist-info → windborne-1.0.7.dist-info}/entry_points.txt +0 -0
- {windborne-1.0.5.dist-info → windborne-1.0.7.dist-info}/top_level.txt +0 -0
windborne/utils.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from .config import CLIENT_ID, API_KEY
|
2
2
|
|
3
|
+
import os
|
3
4
|
import requests
|
4
5
|
import jwt
|
5
6
|
import time
|
@@ -58,6 +59,20 @@ def make_api_request(url, params=None, return_type=None):
|
|
58
59
|
print("--------------------------------------")
|
59
60
|
print("To get an API key, email data@windbornesystems.com.")
|
60
61
|
exit(91)
|
62
|
+
# Check if credentials are swapped
|
63
|
+
elif len(CLIENT_ID) in [32, 35]:
|
64
|
+
print("Your Client ID and API Key are swapped.")
|
65
|
+
print("--------------------------------------")
|
66
|
+
print("Swap them or modify them accordingly to get access to WindBorne API.")
|
67
|
+
print("--------------------------------------")
|
68
|
+
print("You may refer to https://windbornesystems.com/docs/api/cli#introduction\n"
|
69
|
+
"for instructions on how to set your credentials as environment variables for CLI and Code usage\n\n"
|
70
|
+
"and to https://windbornesystems.com/docs/api/pip_data#introduction\n"
|
71
|
+
"for instruction on how to set your credentials for code usage.")
|
72
|
+
print("--------------------------------------")
|
73
|
+
print(f"Current Client ID: {CLIENT_ID}")
|
74
|
+
print(f"Current API Key: {API_KEY}")
|
75
|
+
exit(95)
|
61
76
|
|
62
77
|
# Validate WB_CLIENT_ID format
|
63
78
|
if not (is_valid_uuid_v4(CLIENT_ID) or is_valid_client_id_format(CLIENT_ID)):
|
@@ -263,6 +278,11 @@ def save_csv_json(save_to_file, response, csv_data_key=None):
|
|
263
278
|
response (dict or list): The response data to save.
|
264
279
|
csv_data_key (str, optional): Key to extract data for CSV. Defaults to None.
|
265
280
|
"""
|
281
|
+
# Create directory path if it doesn't exist
|
282
|
+
directory = os.path.dirname(save_to_file)
|
283
|
+
if directory and not os.path.isdir(directory):
|
284
|
+
os.makedirs(directory, exist_ok=True)
|
285
|
+
|
266
286
|
if '.' not in save_to_file:
|
267
287
|
print("You have to provide a file type for your filename.")
|
268
288
|
print("Supported formats:")
|
@@ -317,7 +337,7 @@ def save_csv_json(save_to_file, response, csv_data_key=None):
|
|
317
337
|
print("Unsupported file format. Please use either .json or .csv.")
|
318
338
|
exit(4)
|
319
339
|
|
320
|
-
def convert_to_netcdf(data, curtime, output_filename
|
340
|
+
def convert_to_netcdf(data, curtime, output_filename):
|
321
341
|
# This module outputs data in netcdf format for the WMO ISARRA program. The output format is netcdf
|
322
342
|
# and the style (variable names, file names, etc.) are described here:
|
323
343
|
# https://github.com/synoptic/wmo-uasdc/tree/main/raw_uas_to_netCDF
|
@@ -342,11 +362,16 @@ def convert_to_netcdf(data, curtime, output_filename=None):
|
|
342
362
|
|
343
363
|
# Convert dictionary to list for DataFrame
|
344
364
|
data_list = []
|
345
|
-
|
346
|
-
#
|
347
|
-
|
348
|
-
|
349
|
-
|
365
|
+
if isinstance(data, dict):
|
366
|
+
# If input is dictionary, convert to list
|
367
|
+
for obs_id, obs_data in data.items():
|
368
|
+
clean_data = {k: None if v == 'None' else v for k, v in obs_data.items()}
|
369
|
+
data_list.append(clean_data)
|
370
|
+
else:
|
371
|
+
# If input is already a list
|
372
|
+
for obs_data in data:
|
373
|
+
clean_data = {k: None if v == 'None' else v for k, v in obs_data.items()}
|
374
|
+
data_list.append(clean_data)
|
350
375
|
|
351
376
|
# Put the data in a panda dataframe in order to easily push to xarray then netcdf output
|
352
377
|
df = pd.DataFrame(data_list)
|
@@ -362,16 +387,16 @@ def convert_to_netcdf(data, curtime, output_filename=None):
|
|
362
387
|
|
363
388
|
# Build the filename and save some variables for use later
|
364
389
|
mt = datetime.fromtimestamp(curtime, tz=timezone.utc)
|
390
|
+
|
391
|
+
is_multi_mission = True
|
392
|
+
|
365
393
|
# Handle dropsondes
|
366
394
|
mission_name = str(df['mission_name'].iloc[0]) if (not df.empty and not pd.isna(df['mission_name'].iloc[0])) else ' '
|
395
|
+
# Dropsondes name is ''
|
396
|
+
if mission_name == ' ':
|
397
|
+
is_multi_mission = False
|
367
398
|
|
368
|
-
|
369
|
-
|
370
|
-
if output_filename:
|
371
|
-
output_file = output_filename
|
372
|
-
is_multi_mission = True # we should calculate this directly, rather than relying on the filename
|
373
|
-
else:
|
374
|
-
output_file = f"WindBorne_{mission_name}_{mt.year:04d}-{mt.month:02d}-{mt.day:02d}_{mt.hour:02d}.nc"
|
399
|
+
output_file = output_filename
|
375
400
|
|
376
401
|
# Derived quantities calculated here:
|
377
402
|
|
@@ -476,8 +501,7 @@ def convert_to_netcdf(data, curtime, output_filename=None):
|
|
476
501
|
}
|
477
502
|
ds['mission_name'].attrs = {
|
478
503
|
'long_name': 'Mission name',
|
479
|
-
'description': 'Which balloon collected the data'
|
480
|
-
'_FillValue': ''
|
504
|
+
'description': 'Which balloon collected the data'
|
481
505
|
}
|
482
506
|
|
483
507
|
# Add Global Attributes synonymous across all UASDC providers
|
@@ -553,7 +577,7 @@ def format_little_r(observations):
|
|
553
577
|
"""
|
554
578
|
little_r_records = []
|
555
579
|
|
556
|
-
for
|
580
|
+
for point in observations:
|
557
581
|
# Observation time
|
558
582
|
observation_time = datetime.fromtimestamp(point['timestamp'], tz=timezone.utc)
|
559
583
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
windborne/__init__.py,sha256=aDFnZEPGmulZ-VVAVD-0maK3UFeLl9PxUyxp_qZ85Gk,1894
|
2
|
+
windborne/cli.py,sha256=Qp6wu3ZbXwnpmHa3odr0sjIJ3DOhtraQblUGwKWEKWc,36416
|
3
|
+
windborne/config.py,sha256=FYIBRiIuii5igAFQlOsHUa6u2i1kKnO1yZE7QfQJvUg,1688
|
4
|
+
windborne/data_api.py,sha256=TtOgzD-ONRFswPbAIiMaCANp_IaP4g8OvNExqZ_81iA,63414
|
5
|
+
windborne/forecasts_api.py,sha256=AYuhFRls_XvzuNB55NF0w3y-_ocYwPxmI6C1lIyFkgM,16865
|
6
|
+
windborne/utils.py,sha256=cpQZ79EB8T0Cy5ygwHsbTEE4XjQ0xYX_sN-Ags8AYJw,39718
|
7
|
+
windborne-1.0.7.dist-info/METADATA,sha256=R6AQZUik0LbU1ofYCGFYL8dWWLyzoIoLhyW3TVrP_Ng,1264
|
8
|
+
windborne-1.0.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
9
|
+
windborne-1.0.7.dist-info/entry_points.txt,sha256=j_YrqdCDrCd7p5MIwQ2BYwNXEi95VNANzLRJmcXEg1U,49
|
10
|
+
windborne-1.0.7.dist-info/top_level.txt,sha256=PE9Lauriu5S5REf7JKhXprufZ_V5RiZ_TnfnrLGJrmE,10
|
11
|
+
windborne-1.0.7.dist-info/RECORD,,
|
windborne-1.0.5.dist-info/RECORD
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
windborne/__init__.py,sha256=Dv2RK6qcZa3YDrTo_W_N9YROKOQV1_HFa0OWnZSBKAc,1783
|
2
|
-
windborne/cli.py,sha256=ZNqeIlWqlKE4iCo8kptUH6Wbish0T8O7-Hljt-gEi9Q,31031
|
3
|
-
windborne/config.py,sha256=FYIBRiIuii5igAFQlOsHUa6u2i1kKnO1yZE7QfQJvUg,1688
|
4
|
-
windborne/data_api.py,sha256=dnF54sLPYRU5dect3WxmonbHKUjihfE9dVdpaT-fylk,35560
|
5
|
-
windborne/forecasts_api.py,sha256=AYuhFRls_XvzuNB55NF0w3y-_ocYwPxmI6C1lIyFkgM,16865
|
6
|
-
windborne/utils.py,sha256=UwcuMu2yj5lOR0G-LWSLcsyn024IHP83tkX9WEokTMY,38600
|
7
|
-
windborne-1.0.5.dist-info/METADATA,sha256=ICGFlsRSW6BP3u4bG6GYYFDYhhzIH5VJiDW6xyw0h2k,1264
|
8
|
-
windborne-1.0.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
9
|
-
windborne-1.0.5.dist-info/entry_points.txt,sha256=j_YrqdCDrCd7p5MIwQ2BYwNXEi95VNANzLRJmcXEg1U,49
|
10
|
-
windborne-1.0.5.dist-info/top_level.txt,sha256=PE9Lauriu5S5REf7JKhXprufZ_V5RiZ_TnfnrLGJrmE,10
|
11
|
-
windborne-1.0.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|