disdrodb 0.1.2__py3-none-any.whl → 0.1.4__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 (142) hide show
  1. disdrodb/__init__.py +68 -34
  2. disdrodb/_config.py +5 -4
  3. disdrodb/_version.py +16 -3
  4. disdrodb/accessor/__init__.py +20 -0
  5. disdrodb/accessor/methods.py +125 -0
  6. disdrodb/api/checks.py +177 -24
  7. disdrodb/api/configs.py +3 -3
  8. disdrodb/api/info.py +13 -13
  9. disdrodb/api/io.py +281 -22
  10. disdrodb/api/path.py +184 -195
  11. disdrodb/api/search.py +18 -9
  12. disdrodb/cli/disdrodb_create_summary.py +103 -0
  13. disdrodb/cli/disdrodb_create_summary_station.py +91 -0
  14. disdrodb/cli/disdrodb_run_l0.py +1 -1
  15. disdrodb/cli/disdrodb_run_l0_station.py +1 -1
  16. disdrodb/cli/disdrodb_run_l0a_station.py +1 -1
  17. disdrodb/cli/disdrodb_run_l0b.py +1 -1
  18. disdrodb/cli/disdrodb_run_l0b_station.py +3 -3
  19. disdrodb/cli/disdrodb_run_l0c.py +1 -1
  20. disdrodb/cli/disdrodb_run_l0c_station.py +3 -3
  21. disdrodb/cli/disdrodb_run_l1_station.py +2 -2
  22. disdrodb/cli/disdrodb_run_l2e_station.py +2 -2
  23. disdrodb/cli/disdrodb_run_l2m_station.py +2 -2
  24. disdrodb/configs.py +149 -4
  25. disdrodb/constants.py +61 -0
  26. disdrodb/data_transfer/download_data.py +127 -11
  27. disdrodb/etc/configs/attributes.yaml +339 -0
  28. disdrodb/etc/configs/encodings.yaml +473 -0
  29. disdrodb/etc/products/L1/global.yaml +13 -0
  30. disdrodb/etc/products/L2E/10MIN.yaml +12 -0
  31. disdrodb/etc/products/L2E/1MIN.yaml +1 -0
  32. disdrodb/etc/products/L2E/global.yaml +22 -0
  33. disdrodb/etc/products/L2M/10MIN.yaml +12 -0
  34. disdrodb/etc/products/L2M/GAMMA_ML.yaml +8 -0
  35. disdrodb/etc/products/L2M/NGAMMA_GS_LOG_ND_MAE.yaml +6 -0
  36. disdrodb/etc/products/L2M/NGAMMA_GS_ND_MAE.yaml +6 -0
  37. disdrodb/etc/products/L2M/NGAMMA_GS_Z_MAE.yaml +6 -0
  38. disdrodb/etc/products/L2M/global.yaml +26 -0
  39. disdrodb/issue/writer.py +2 -0
  40. disdrodb/l0/__init__.py +13 -0
  41. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +4 -4
  42. disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
  43. disdrodb/l0/configs/PARSIVEL/l0b_encodings.yml +3 -3
  44. disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +1 -1
  45. disdrodb/l0/configs/PARSIVEL2/l0b_cf_attrs.yml +5 -5
  46. disdrodb/l0/configs/PARSIVEL2/l0b_encodings.yml +3 -3
  47. disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +1 -1
  48. disdrodb/l0/configs/PWS100/l0b_cf_attrs.yml +4 -4
  49. disdrodb/l0/configs/PWS100/raw_data_format.yml +1 -1
  50. disdrodb/l0/l0a_processing.py +37 -32
  51. disdrodb/l0/l0b_nc_processing.py +118 -8
  52. disdrodb/l0/l0b_processing.py +30 -65
  53. disdrodb/l0/l0c_processing.py +369 -259
  54. disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +7 -0
  55. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_LPM_NC.py +66 -0
  56. disdrodb/l0/readers/LPM/SLOVENIA/{CRNI_VRH.py → UL.py} +3 -0
  57. disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +195 -0
  58. disdrodb/l0/readers/PARSIVEL/GPM/PIERS.py +0 -2
  59. disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +4 -1
  60. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
  61. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
  62. disdrodb/l0/readers/PARSIVEL2/ARM/ARM_PARSIVEL2.py +4 -0
  63. disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +168 -0
  64. disdrodb/l0/readers/PARSIVEL2/CANADA/UQAM_NC.py +69 -0
  65. disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +165 -0
  66. disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +69 -0
  67. disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +255 -134
  68. disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +525 -0
  69. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +1 -1
  70. disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +9 -7
  71. disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +1 -1
  72. disdrodb/l0/readers/PARSIVEL2/KIT/TEAMX.py +123 -0
  73. disdrodb/l0/readers/PARSIVEL2/{NETHERLANDS/DELFT.py → MPI/BCO_PARSIVEL2.py} +41 -71
  74. disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +220 -0
  75. disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +120 -0
  76. disdrodb/l0/readers/PARSIVEL2/NASA/LPVEX.py +109 -0
  77. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +1 -0
  78. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
  79. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +126 -0
  80. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_PIPS.py +165 -0
  81. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
  82. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +20 -12
  83. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +5 -0
  84. disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +144 -0
  85. disdrodb/l0/readers/PARSIVEL2/SPAIN/CR1000DL.py +201 -0
  86. disdrodb/l0/readers/PARSIVEL2/SPAIN/LIAISE.py +137 -0
  87. disdrodb/l0/readers/PARSIVEL2/USA/C3WE.py +146 -0
  88. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +105 -99
  89. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +151 -0
  90. disdrodb/l1/__init__.py +5 -0
  91. disdrodb/l1/fall_velocity.py +46 -0
  92. disdrodb/l1/filters.py +34 -20
  93. disdrodb/l1/processing.py +46 -45
  94. disdrodb/l1/resampling.py +77 -66
  95. disdrodb/l1_env/routines.py +18 -3
  96. disdrodb/l2/__init__.py +7 -0
  97. disdrodb/l2/empirical_dsd.py +58 -10
  98. disdrodb/l2/processing.py +268 -117
  99. disdrodb/metadata/checks.py +132 -125
  100. disdrodb/metadata/standards.py +3 -1
  101. disdrodb/psd/fitting.py +631 -345
  102. disdrodb/psd/models.py +9 -6
  103. disdrodb/routines/__init__.py +54 -0
  104. disdrodb/{l0/routines.py → routines/l0.py} +316 -355
  105. disdrodb/{l1/routines.py → routines/l1.py} +76 -116
  106. disdrodb/routines/l2.py +1019 -0
  107. disdrodb/{routines.py → routines/wrappers.py} +98 -10
  108. disdrodb/scattering/__init__.py +16 -4
  109. disdrodb/scattering/axis_ratio.py +61 -37
  110. disdrodb/scattering/permittivity.py +504 -0
  111. disdrodb/scattering/routines.py +746 -184
  112. disdrodb/summary/__init__.py +17 -0
  113. disdrodb/summary/routines.py +4196 -0
  114. disdrodb/utils/archiving.py +434 -0
  115. disdrodb/utils/attrs.py +68 -125
  116. disdrodb/utils/cli.py +5 -5
  117. disdrodb/utils/compression.py +30 -1
  118. disdrodb/utils/dask.py +121 -9
  119. disdrodb/utils/dataframe.py +61 -7
  120. disdrodb/utils/decorators.py +31 -0
  121. disdrodb/utils/directories.py +35 -15
  122. disdrodb/utils/encoding.py +37 -19
  123. disdrodb/{l2 → utils}/event.py +15 -173
  124. disdrodb/utils/logger.py +14 -7
  125. disdrodb/utils/manipulations.py +81 -0
  126. disdrodb/utils/routines.py +166 -0
  127. disdrodb/utils/subsetting.py +214 -0
  128. disdrodb/utils/time.py +35 -177
  129. disdrodb/utils/writer.py +20 -7
  130. disdrodb/utils/xarray.py +5 -4
  131. disdrodb/viz/__init__.py +13 -0
  132. disdrodb/viz/plots.py +398 -0
  133. {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/METADATA +4 -3
  134. {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/RECORD +139 -98
  135. {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/entry_points.txt +2 -0
  136. disdrodb/l1/encoding_attrs.py +0 -642
  137. disdrodb/l2/processing_options.py +0 -213
  138. disdrodb/l2/routines.py +0 -868
  139. /disdrodb/l0/readers/PARSIVEL/SLOVENIA/{UL_FGG.py → UL.py} +0 -0
  140. {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/WHEEL +0 -0
  141. {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/licenses/LICENSE +0 -0
  142. {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/top_level.txt +0 -0
disdrodb/api/path.py CHANGED
@@ -20,11 +20,12 @@
20
20
  import os
21
21
 
22
22
  from disdrodb.configs import get_data_archive_dir, get_metadata_archive_dir
23
+ from disdrodb.constants import ARCHIVE_VERSION
23
24
  from disdrodb.utils.directories import check_directory_exists
24
25
  from disdrodb.utils.time import (
25
26
  ensure_sample_interval_in_seconds,
26
27
  get_file_start_end_time,
27
- seconds_to_acronym,
28
+ seconds_to_temporal_resolution,
28
29
  )
29
30
 
30
31
  ####--------------------------------------------------------------------------.
@@ -62,14 +63,13 @@ def define_disdrodb_path(
62
63
  The campaign name.
63
64
  check_exists : bool, optional
64
65
  Whether to check if the directory exists. The default value is ``True``.
66
+ Raise error if the directory does not exist.
65
67
 
66
68
  Returns
67
69
  -------
68
70
  dir_path : str
69
71
  Directory path
70
72
  """
71
- from disdrodb import ARCHIVE_VERSION
72
-
73
73
  if len(campaign_name) > 0 and len(data_source) == 0:
74
74
  raise ValueError("If campaign_name is specified, data_source must be specified.")
75
75
 
@@ -82,7 +82,7 @@ def define_disdrodb_path(
82
82
  dir_path = os.path.join(archive_dir, ARCHIVE_VERSION, data_source, campaign_name)
83
83
  if check_exists:
84
84
  check_directory_exists(dir_path)
85
- return dir_path
85
+ return os.path.normpath(dir_path)
86
86
 
87
87
 
88
88
  def define_data_source_dir(
@@ -108,6 +108,7 @@ def define_data_source_dir(
108
108
  If not specified, the path specified in the DISDRODB active configuration will be used.
109
109
  check_exists : bool, optional
110
110
  Whether to check if the directory exists. The default value is ``False``.
111
+ Raise error if the directory does not exist.
111
112
 
112
113
  Returns
113
114
  -------
@@ -349,7 +350,56 @@ def define_config_dir(product):
349
350
  #### Directory/Filepaths L0A and L0B products
350
351
 
351
352
 
352
- def define_file_folder_path(obj, data_dir, folder_partitioning):
353
+ def define_partitioning_tree(time, folder_partitioning):
354
+ """Define the time directory tree given a timestep.
355
+
356
+ Parameters
357
+ ----------
358
+ time : datetime.datetime
359
+ Timestep.
360
+ folder_partitioning : str or None
361
+ Define the subdirectory structure where saving files.
362
+ Allowed values are:
363
+ - None: Files are saved directly in data_dir.
364
+ - "year": Files are saved under a subdirectory for the year.
365
+ - "year/month": Files are saved under subdirectories for year and month.
366
+ - "year/month/day": Files are saved under subdirectories for year, month and day
367
+ - "year/month_name": Files are stored under subdirectories by year and month name
368
+ - "year/quarter": Files are saved under subdirectories for year and quarter.
369
+
370
+ Returns
371
+ -------
372
+ str
373
+ A time partitioned directory tree.
374
+ """
375
+ if folder_partitioning == "":
376
+ return ""
377
+ if folder_partitioning == "year":
378
+ year = str(time.year)
379
+ return year
380
+ if folder_partitioning == "year/month":
381
+ year = str(time.year)
382
+ month = str(time.month).zfill(2)
383
+ return os.path.join(year, month)
384
+ if folder_partitioning == "year/month/day":
385
+ year = str(time.year)
386
+ month = str(time.month).zfill(2)
387
+ day = str(time.day).zfill(2)
388
+ return os.path.join(year, month, day)
389
+ if folder_partitioning == "year/month_name":
390
+ year = str(time.year)
391
+ month = time.strftime("%B")
392
+ return os.path.join(year, month)
393
+ if folder_partitioning == "year/quarter":
394
+ year = str(time.year)
395
+ # Calculate quarter: months 1-3 => Q1, 4-6 => Q2, etc.
396
+ quarter = (time.month - 1) // 3 + 1
397
+ quarter_dir = f"Q{quarter}"
398
+ return os.path.join(year, quarter_dir)
399
+ raise NotImplementedError(f"Unrecognized '{folder_partitioning}' folder partitioning scheme.")
400
+
401
+
402
+ def define_file_folder_path(obj, dir_path, folder_partitioning):
353
403
  """
354
404
  Define the folder path where saving a file based on the dataset's starting time.
355
405
 
@@ -357,12 +407,13 @@ def define_file_folder_path(obj, data_dir, folder_partitioning):
357
407
  ----------
358
408
  ds : xarray.Dataset or pandas.DataFrame
359
409
  The object containing time information.
360
- data_dir : str
410
+ dir : str
361
411
  Directory within the DISDRODB Data Archive where DISDRODB product files are to be saved.
412
+ It can be a product directory or a logs directory.
362
413
  folder_partitioning : str or None
363
414
  Define the subdirectory structure where saving files.
364
415
  Allowed values are:
365
- - None: Files are saved directly in data_dir.
416
+ - None or "": Files are saved directly in data_dir.
366
417
  - "year": Files are saved under a subdirectory for the year.
367
418
  - "year/month": Files are saved under subdirectories for year and month.
368
419
  - "year/month/day": Files are saved under subdirectories for year, month and day
@@ -382,32 +433,9 @@ def define_file_folder_path(obj, data_dir, folder_partitioning):
382
433
  # Retrieve the starting time from the dataset.
383
434
  starting_time, _ = get_file_start_end_time(obj)
384
435
 
385
- # Build the folder path based on the chosen partition scheme.
386
- if folder_partitioning == "":
387
- return data_dir
388
- if folder_partitioning == "year":
389
- year = str(starting_time.year)
390
- return os.path.join(data_dir, year)
391
- if folder_partitioning == "year/month":
392
- year = str(starting_time.year)
393
- month = str(starting_time.month).zfill(2)
394
- return os.path.join(data_dir, year, month)
395
- if folder_partitioning == "year/month/day":
396
- year = str(starting_time.year)
397
- month = str(starting_time.month).zfill(2)
398
- day = str(starting_time.day).zfill(2)
399
- return os.path.join(data_dir, year, month, day)
400
- if folder_partitioning == "year/month_name":
401
- year = str(starting_time.year)
402
- month = str(starting_time.month_name())
403
- return os.path.join(data_dir, year, month)
404
- if folder_partitioning == "year/quarter":
405
- year = str(starting_time.year)
406
- # Calculate quarter: months 1-3 => Q1, 4-6 => Q2, etc.
407
- quarter = (starting_time.month - 1) // 3 + 1
408
- quarter_dir = f"Q{quarter}"
409
- return os.path.join(data_dir, year, quarter_dir)
410
- raise NotImplementedError(f"Unrecognized '{folder_partitioning}' folder partitioning scheme.")
436
+ # Build the folder path based on the chosen partition scheme
437
+ partitioning_tree = define_partitioning_tree(time=starting_time, folder_partitioning=folder_partitioning)
438
+ return os.path.normpath(os.path.join(dir_path, partitioning_tree))
411
439
 
412
440
 
413
441
  def define_product_dir_tree(
@@ -448,17 +476,16 @@ def define_product_dir_tree(
448
476
  sample_interval = product_kwargs.get("sample_interval")
449
477
  check_rolling(rolling)
450
478
  check_sample_interval(sample_interval)
451
- sample_interval_acronym = define_accumulation_acronym(seconds=sample_interval, rolling=rolling)
452
- return os.path.join(sample_interval_acronym)
453
- if product == "L2M":
454
- rolling = product_kwargs.get("rolling")
455
- sample_interval = product_kwargs.get("sample_interval")
456
- model_name = product_kwargs.get("model_name")
457
- check_rolling(rolling)
458
- check_sample_interval(sample_interval)
459
- sample_interval_acronym = define_accumulation_acronym(seconds=sample_interval, rolling=rolling)
460
- return os.path.join(model_name, sample_interval_acronym)
461
- raise ValueError(f"The product {product} is not defined.")
479
+ temporal_resolution = define_temporal_resolution(seconds=sample_interval, rolling=rolling)
480
+ return os.path.join(temporal_resolution)
481
+ # L2M if product == "L2M":
482
+ rolling = product_kwargs.get("rolling")
483
+ sample_interval = product_kwargs.get("sample_interval")
484
+ model_name = product_kwargs.get("model_name")
485
+ check_rolling(rolling)
486
+ check_sample_interval(sample_interval)
487
+ temporal_resolution = define_temporal_resolution(seconds=sample_interval, rolling=rolling)
488
+ return os.path.join(model_name, temporal_resolution)
462
489
 
463
490
 
464
491
  def define_logs_dir(
@@ -504,7 +531,7 @@ def define_logs_dir(
504
531
  product=product,
505
532
  **product_kwargs,
506
533
  )
507
- logs_dir = os.path.join(campaign_dir, "logs", "files", product, product_dir_tree, station_name)
534
+ logs_dir = os.path.normpath(os.path.join(campaign_dir, "logs", "files", product, product_dir_tree, station_name))
508
535
  if check_exists:
509
536
  check_directory_exists(logs_dir)
510
537
  return str(logs_dir)
@@ -618,7 +645,7 @@ def define_data_dir(
618
645
  **product_kwargs,
619
646
  )
620
647
  # Define data directory
621
- data_dir = os.path.join(station_dir, product_dir_tree)
648
+ data_dir = os.path.normpath(os.path.join(station_dir, product_dir_tree))
622
649
  # Check if directory exists
623
650
  if check_exists:
624
651
  check_directory_exists(data_dir)
@@ -629,15 +656,15 @@ def define_data_dir(
629
656
  #### Filenames for DISDRODB products
630
657
 
631
658
 
632
- def define_accumulation_acronym(seconds, rolling):
633
- """Define the accumulation acronnym.
659
+ def define_temporal_resolution(seconds, rolling):
660
+ """Define the DISDRODB product temporal resolution.
634
661
 
635
- Prefix the accumulation interval acronym with ROLL if rolling=True.
662
+ Prefix the measurement interval with ROLL if rolling=True.
636
663
  """
637
- accumulation_acronym = seconds_to_acronym(seconds)
664
+ temporal_resolution = seconds_to_temporal_resolution(seconds)
638
665
  if rolling:
639
- accumulation_acronym = f"ROLL{accumulation_acronym}"
640
- return accumulation_acronym
666
+ temporal_resolution = f"ROLL{temporal_resolution}"
667
+ return temporal_resolution
641
668
 
642
669
 
643
670
  ####--------------------------------------------------------------------------.
@@ -649,7 +676,8 @@ def define_filename(
649
676
  campaign_name: str,
650
677
  station_name: str,
651
678
  # Filename options
652
- obj=None,
679
+ start_time=None,
680
+ end_time=None,
653
681
  add_version=True,
654
682
  add_time_period=True,
655
683
  add_extension=True,
@@ -663,19 +691,22 @@ def define_filename(
663
691
 
664
692
  Parameters
665
693
  ----------
666
- obj : xarray.Dataset or pandas.DataFrame
667
- xarray Dataset or pandas DataFrame.
668
- Required if add_time_period = True.
669
694
  campaign_name : str
670
695
  Name of the campaign.
671
696
  station_name : str
672
697
  Name of the station.
698
+ start_time : datetime.datatime, optional
699
+ Start time.
700
+ Required if add_time_period = True.
701
+ end_time : datetime.datatime, optional
702
+ End time.
703
+ Required if add_time_period = True.
673
704
  sample_interval : int, optional
674
705
  The sampling interval in seconds of the product.
675
- It must be specified only for product L2E and L2M !
706
+ It must be specified only for product L0C, L1, L2E and L2M !
676
707
  rolling : bool, optional
677
708
  Whether the dataset has been resampled by aggregating or rolling.
678
- It must be specified only for product L2E and L2M !
709
+ It must be specified only for product L1, L2E and L2M !
679
710
  model_name : str
680
711
  The model name of the fitted statistical distribution for the DSD.
681
712
  It must be specified only for product L2M !
@@ -685,32 +716,37 @@ def define_filename(
685
716
  str
686
717
  L0B file name.
687
718
  """
688
- from disdrodb import ARCHIVE_VERSION
689
719
  from disdrodb.api.checks import check_product, check_product_kwargs
690
720
 
691
721
  product = check_product(product)
692
722
  product_kwargs = check_product_kwargs(product, product_kwargs)
693
723
 
694
- # -----------------------------------------.
695
- # TODO: Define sample_interval_acronym
696
- # - ADD sample_interval_acronym also to L0A and L0B
697
- # - Add sample_interval_acronym also to L0C and L1
724
+ if add_time_period and (start_time is None or end_time is None):
725
+ raise ValueError("If add_time_period=True, specify start_time and end_time.")
698
726
 
699
727
  # -----------------------------------------.
700
- # Define product acronym
701
- product_acronym = f"{product}"
728
+ # Define product name
729
+ product_name = f"{product}"
730
+
731
+ # L0C ... sample interval known only per-file
732
+ # L1 ... in future known a priori
733
+ # if product in ["L1"]:
734
+ # # TODO: HACK FOR CURRENT L0C and L1 log files in create_product_logs
735
+ # sample_interval = product_kwargs.get("sample_interval", 0)
736
+ # temporal_resolution = define_temporal_resolution(seconds=sample_interval, rolling=False)
737
+ # product_name = f"{product}.{temporal_resolution}"
702
738
  if product in ["L2E", "L2M"]:
703
739
  rolling = product_kwargs.get("rolling")
704
740
  sample_interval = product_kwargs.get("sample_interval")
705
- sample_interval_acronym = define_accumulation_acronym(seconds=sample_interval, rolling=rolling)
706
- product_acronym = f"L2E.{sample_interval_acronym}"
741
+ temporal_resolution = define_temporal_resolution(seconds=sample_interval, rolling=rolling)
742
+ product_name = f"{product}.{temporal_resolution}"
707
743
  if product in ["L2M"]:
708
744
  model_name = product_kwargs.get("model_name")
709
- product_acronym = f"L2M_{model_name}.{sample_interval_acronym}"
745
+ product_name = f"L2M_{model_name}.{temporal_resolution}"
710
746
 
711
747
  # -----------------------------------------.
712
748
  # Define base filename
713
- filename = f"{product_acronym}.{campaign_name}.{station_name}"
749
+ filename = f"{product_name}.{campaign_name}.{station_name}"
714
750
 
715
751
  # -----------------------------------------.
716
752
  # Add prefix
@@ -720,10 +756,9 @@ def define_filename(
720
756
  # -----------------------------------------.
721
757
  # Add time period information
722
758
  if add_time_period:
723
- starting_time, ending_time = get_file_start_end_time(obj)
724
- starting_time = starting_time.strftime("%Y%m%d%H%M%S")
725
- ending_time = ending_time.strftime("%Y%m%d%H%M%S")
726
- filename = f"{filename}.s{starting_time}.e{ending_time}"
759
+ start_time = start_time.strftime("%Y%m%d%H%M%S")
760
+ end_time = end_time.strftime("%Y%m%d%H%M%S")
761
+ filename = f"{filename}.s{start_time}.e{end_time}"
727
762
 
728
763
  # -----------------------------------------.
729
764
  # Add product version
@@ -759,133 +794,97 @@ def define_l0a_filename(df, campaign_name: str, station_name: str) -> str:
759
794
  str
760
795
  L0A file name.
761
796
  """
762
- from disdrodb import ARCHIVE_VERSION
763
-
764
797
  starting_time, ending_time = get_file_start_end_time(df)
765
- starting_time = starting_time.strftime("%Y%m%d%H%M%S")
766
- ending_time = ending_time.strftime("%Y%m%d%H%M%S")
767
- version = ARCHIVE_VERSION
768
- filename = f"L0A.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.parquet"
798
+ filename = define_filename(
799
+ product="L0A",
800
+ campaign_name=campaign_name,
801
+ station_name=station_name,
802
+ # Filename options
803
+ start_time=starting_time,
804
+ end_time=ending_time,
805
+ add_version=True,
806
+ add_time_period=True,
807
+ add_extension=True,
808
+ )
769
809
  return filename
770
810
 
771
811
 
772
812
  def define_l0b_filename(ds, campaign_name: str, station_name: str) -> str:
773
- """Define L0B file name.
774
-
775
- Parameters
776
- ----------
777
- ds : xarray.Dataset
778
- L0B xarray Dataset.
779
- campaign_name : str
780
- Name of the campaign.
781
- station_name : str
782
- Name of the station.
783
-
784
- Returns
785
- -------
786
- str
787
- L0B file name.
788
- """
789
- from disdrodb import ARCHIVE_VERSION
790
-
813
+ """Define L0B file name."""
791
814
  starting_time, ending_time = get_file_start_end_time(ds)
792
- starting_time = starting_time.strftime("%Y%m%d%H%M%S")
793
- ending_time = ending_time.strftime("%Y%m%d%H%M%S")
794
- version = ARCHIVE_VERSION
795
- filename = f"L0B.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
815
+ filename = define_filename(
816
+ product="L0B",
817
+ campaign_name=campaign_name,
818
+ station_name=station_name,
819
+ # Filename options
820
+ start_time=starting_time,
821
+ end_time=ending_time,
822
+ add_version=True,
823
+ add_time_period=True,
824
+ add_extension=True,
825
+ )
796
826
  return filename
797
827
 
798
828
 
799
829
  def define_l0c_filename(ds, campaign_name: str, station_name: str) -> str:
800
- """Define L0C file name.
801
-
802
- Parameters
803
- ----------
804
- ds : xarray.Dataset
805
- L0B xarray Dataset.
806
- campaign_name : str
807
- Name of the campaign.
808
- station_name : str
809
- Name of the station.
810
-
811
- Returns
812
- -------
813
- str
814
- L0B file name.
815
- """
816
- from disdrodb import ARCHIVE_VERSION
817
-
818
- # TODO: add sample_interval as argument
830
+ """Define L0C file name."""
831
+ # TODO: add sample_interval as function argument s
819
832
  sample_interval = int(ensure_sample_interval_in_seconds(ds["sample_interval"]).data.item())
820
- sample_interval_acronym = define_accumulation_acronym(sample_interval, rolling=False)
833
+ temporal_resolution = define_temporal_resolution(sample_interval, rolling=False)
821
834
  starting_time, ending_time = get_file_start_end_time(ds)
822
835
  starting_time = starting_time.strftime("%Y%m%d%H%M%S")
823
836
  ending_time = ending_time.strftime("%Y%m%d%H%M%S")
824
837
  version = ARCHIVE_VERSION
825
- filename = (
826
- f"L0C.{sample_interval_acronym}.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
827
- )
838
+ filename = f"L0C.{temporal_resolution}.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
828
839
  return filename
829
840
 
830
841
 
831
842
  def define_l1_filename(ds, campaign_name, station_name: str) -> str:
832
- """Define L1 file name.
833
-
834
- Parameters
835
- ----------
836
- ds : xarray.Dataset
837
- L1 xarray Dataset.
838
- campaign_name : str
839
- Name of the campaign.
840
- station_name : str
841
- Name of the station.
843
+ """Define L1 file name."""
844
+ # TODO: add sample_interval and rolling as function argument
842
845
 
843
- Returns
844
- -------
845
- str
846
- L1 file name.
847
- """
848
- from disdrodb import ARCHIVE_VERSION
849
-
850
- # TODO: add sample_interval as argument
846
+ starting_time, ending_time = get_file_start_end_time(ds)
851
847
  sample_interval = int(ensure_sample_interval_in_seconds(ds["sample_interval"]).data.item())
852
- sample_interval_acronym = define_accumulation_acronym(sample_interval, rolling=False)
848
+ temporal_resolution = define_temporal_resolution(sample_interval, rolling=False)
853
849
  starting_time, ending_time = get_file_start_end_time(ds)
854
850
  starting_time = starting_time.strftime("%Y%m%d%H%M%S")
855
851
  ending_time = ending_time.strftime("%Y%m%d%H%M%S")
856
852
  version = ARCHIVE_VERSION
857
- filename = (
858
- f"L1.{sample_interval_acronym}.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
859
- )
853
+ filename = f"L1.{temporal_resolution}.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
854
+
855
+ # filename = define_filename(
856
+ # product="L1",
857
+ # campaign_name=campaign_name,
858
+ # station_name=station_name,
859
+ # # Filename options
860
+ # start_time=starting_time,
861
+ # end_time=ending_time,
862
+ # add_version=True,
863
+ # add_time_period=True,
864
+ # add_extension=True,
865
+ # # Product options
866
+ # # sample_interval=sample_interval,
867
+ # # rolling=rolling,
868
+ # )
860
869
  return filename
861
870
 
862
871
 
863
872
  def define_l2e_filename(ds, campaign_name: str, station_name: str, sample_interval: int, rolling: bool) -> str:
864
- """Define L2E file name.
865
-
866
- Parameters
867
- ----------
868
- ds : xarray.Dataset
869
- L1 xarray Dataset
870
- campaign_name : str
871
- Name of the campaign.
872
- station_name : str
873
- Name of the station
874
-
875
- Returns
876
- -------
877
- str
878
- L0B file name.
879
- """
880
- from disdrodb import ARCHIVE_VERSION
881
-
882
- sample_interval_acronym = define_accumulation_acronym(seconds=sample_interval, rolling=rolling)
873
+ """Define L2E file name."""
883
874
  starting_time, ending_time = get_file_start_end_time(ds)
884
- starting_time = starting_time.strftime("%Y%m%d%H%M%S")
885
- ending_time = ending_time.strftime("%Y%m%d%H%M%S")
886
- version = ARCHIVE_VERSION
887
- filename = (
888
- f"L2E.{sample_interval_acronym}.{campaign_name}.{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
875
+ filename = define_filename(
876
+ product="L2E",
877
+ campaign_name=campaign_name,
878
+ station_name=station_name,
879
+ # Filename options
880
+ start_time=starting_time,
881
+ end_time=ending_time,
882
+ add_version=True,
883
+ add_time_period=True,
884
+ add_extension=True,
885
+ # Product options
886
+ sample_interval=sample_interval,
887
+ rolling=rolling,
889
888
  )
890
889
  return filename
891
890
 
@@ -898,31 +897,21 @@ def define_l2m_filename(
898
897
  rolling: bool,
899
898
  model_name: str,
900
899
  ) -> str:
901
- """Define L2M file name.
902
-
903
- Parameters
904
- ----------
905
- ds : xarray.Dataset
906
- L1 xarray Dataset
907
- campaign_name : str
908
- Name of the campaign.
909
- station_name : str
910
- Name of the station
911
-
912
- Returns
913
- -------
914
- str
915
- L0B file name.
916
- """
917
- from disdrodb import ARCHIVE_VERSION
918
-
919
- sample_interval_acronym = define_accumulation_acronym(seconds=sample_interval, rolling=rolling)
900
+ """Define L2M file name."""
920
901
  starting_time, ending_time = get_file_start_end_time(ds)
921
- starting_time = starting_time.strftime("%Y%m%d%H%M%S")
922
- ending_time = ending_time.strftime("%Y%m%d%H%M%S")
923
- version = ARCHIVE_VERSION
924
- filename = (
925
- f"L2M_{model_name}.{sample_interval_acronym}.{campaign_name}."
926
- + f"{station_name}.s{starting_time}.e{ending_time}.{version}.nc"
902
+ filename = define_filename(
903
+ product="L2M",
904
+ campaign_name=campaign_name,
905
+ station_name=station_name,
906
+ # Filename options
907
+ start_time=starting_time,
908
+ end_time=ending_time,
909
+ add_version=True,
910
+ add_time_period=True,
911
+ add_extension=True,
912
+ # Product options
913
+ sample_interval=sample_interval,
914
+ rolling=rolling,
915
+ model_name=model_name,
927
916
  )
928
917
  return filename
disdrodb/api/search.py CHANGED
@@ -16,14 +16,15 @@ from disdrodb.api.path import (
16
16
  define_station_dir,
17
17
  )
18
18
  from disdrodb.configs import get_data_archive_dir, get_metadata_archive_dir
19
- from disdrodb.utils.directories import contains_files, contains_netcdf_or_parquet_files
19
+ from disdrodb.constants import PRODUCTS_REQUIREMENTS
20
+ from disdrodb.utils.directories import contains_files, contains_netcdf_or_parquet_files, list_directories, list_files
20
21
  from disdrodb.utils.yaml import read_yaml
21
22
 
23
+ ####-------------------------------------------------------------------------
24
+
22
25
 
23
26
  def get_required_product(product):
24
27
  """Determine the required product for input product processing."""
25
- from disdrodb import PRODUCTS_REQUIREMENTS
26
-
27
28
  # Check input
28
29
  check_product(product)
29
30
  # Determine required product
@@ -32,12 +33,13 @@ def get_required_product(product):
32
33
 
33
34
 
34
35
  ####-------------------------------------------------------------------------
35
- #### List DISDRODB infrastructure directories
36
+ #### List DISDRODB Metadata directories
36
37
 
37
38
 
38
39
  def list_data_sources(metadata_archive_dir, data_sources=None, invalid_fields_policy="raise"):
39
40
  """List data sources names in the DISDRODB Metadata Archive."""
40
- available_data_sources = os.listdir(os.path.join(metadata_archive_dir, "METADATA"))
41
+ path = os.path.join(metadata_archive_dir, "METADATA")
42
+ available_data_sources = sorted(list_directories(path, return_paths=False))
41
43
  # Filter by optionally specified data_sources
42
44
  if data_sources is not None:
43
45
  available_data_sources = check_valid_fields(
@@ -52,7 +54,7 @@ def list_data_sources(metadata_archive_dir, data_sources=None, invalid_fields_po
52
54
 
53
55
  def _list_campaign_names(metadata_archive_dir, data_source):
54
56
  data_source_dir = define_data_source_dir(metadata_archive_dir, product="METADATA", data_source=data_source)
55
- campaign_names = os.listdir(data_source_dir)
57
+ campaign_names = sorted(list_directories(data_source_dir, return_paths=False))
56
58
  return campaign_names
57
59
 
58
60
 
@@ -109,7 +111,7 @@ def _list_station_names(metadata_archive_dir, data_source, campaign_name):
109
111
  data_source=data_source,
110
112
  campaign_name=campaign_name,
111
113
  )
112
- metadata_filenames = os.listdir(metadata_dir)
114
+ metadata_filenames = sorted(list_files(metadata_dir, glob_pattern="*.yml", return_paths=False))
113
115
  station_names = [fname.replace(".yml", "").replace(".yaml", "") for fname in metadata_filenames]
114
116
  return station_names
115
117
 
@@ -167,6 +169,10 @@ def list_station_names(
167
169
  return station_names
168
170
 
169
171
 
172
+ ####-------------------------------------------------------------------------
173
+ #### Filtering utilities for available_stations
174
+
175
+
170
176
  def _finalize_output(list_info, return_tuple):
171
177
  # - Return the (data_source, campaign_name, station_name) tuple
172
178
  if return_tuple:
@@ -228,7 +234,6 @@ def keep_list_info_elements_with_product_data(data_archive_dir, product, list_in
228
234
  checking_function = contains_files if product == "RAW" else contains_netcdf_or_parquet_files
229
235
 
230
236
  # Check presence of data for each station
231
- # TODO: - In parallel over stations to speed up ?
232
237
  list_info_with_product_data = []
233
238
  for data_source, campaign_name, station_name in list_info:
234
239
  data_dir = define_data_dir(
@@ -362,10 +367,11 @@ def available_stations(
362
367
  metadata_archive_dir = get_metadata_archive_dir(metadata_archive_dir)
363
368
  product = check_product(product) if product is not None else None
364
369
  invalid_fields_policy = check_invalid_fields_policy(invalid_fields_policy)
370
+
365
371
  # Retrieve available stations from the Metadata Archive
366
372
  # - Raise error if no stations availables !
367
373
  list_info = list_station_names(
368
- metadata_archive_dir,
374
+ metadata_archive_dir=metadata_archive_dir,
369
375
  data_sources=data_sources,
370
376
  campaign_names=campaign_names,
371
377
  station_names=station_names,
@@ -484,3 +490,6 @@ def available_campaigns(
484
490
  campaign_names = [info[1] for info in list_info]
485
491
  campaign_names = np.unique(campaign_names).tolist()
486
492
  return campaign_names
493
+
494
+
495
+ ####-------------------------------------------------------------------------