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/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=None):
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
- for obs_id, obs_data in data.items():
346
- # Convert 'None' strings to None type
347
- clean_data = {k: None if v == 'None' else v for k, v in obs_data.items()}
348
-
349
- data_list.append(clean_data)
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
- is_multi_mission = False
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 obs_id, point in observations:
580
+ for point in observations:
557
581
  # Observation time
558
582
  observation_time = datetime.fromtimestamp(point['timestamp'], tz=timezone.utc)
559
583
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: windborne
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: A Python library for interacting with WindBorne Data and Forecasts API
5
5
  Author-email: WindBorne Systems <data@windbornesystems.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -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,,
@@ -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,,