cdss-ruby 0.1.0

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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +86 -0
  3. data/CHANGELOG.md +5 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/README.md +101 -0
  6. data/Rakefile +26 -0
  7. data/docs/Cdss/AdminCalls.html +399 -0
  8. data/docs/Cdss/Analysis.html +972 -0
  9. data/docs/Cdss/Client.html +581 -0
  10. data/docs/Cdss/Climate.html +1257 -0
  11. data/docs/Cdss/Concerns/LogReadingAttributes.html +406 -0
  12. data/docs/Cdss/Concerns/WellReadingAttributes.html +414 -0
  13. data/docs/Cdss/Concerns.html +117 -0
  14. data/docs/Cdss/GroundWater.html +945 -0
  15. data/docs/Cdss/Models/AdminCall.html +252 -0
  16. data/docs/Cdss/Models/Analysis.html +397 -0
  17. data/docs/Cdss/Models/CallAnalysis.html +140 -0
  18. data/docs/Cdss/Models/ClimateStation.html +249 -0
  19. data/docs/Cdss/Models/DiversionRecord.html +248 -0
  20. data/docs/Cdss/Models/Reading.html +301 -0
  21. data/docs/Cdss/Models/ReferenceTable.html +339 -0
  22. data/docs/Cdss/Models/RouteAnalysis.html +140 -0
  23. data/docs/Cdss/Models/SourceRoute.html +140 -0
  24. data/docs/Cdss/Models/Station.html +248 -0
  25. data/docs/Cdss/Models/Structure.html +259 -0
  26. data/docs/Cdss/Models/WaterClass.html +249 -0
  27. data/docs/Cdss/Models/WaterRight.html +255 -0
  28. data/docs/Cdss/Models/Well.html +251 -0
  29. data/docs/Cdss/Models.html +117 -0
  30. data/docs/Cdss/Parser.html +2155 -0
  31. data/docs/Cdss/Parsers/AdminCallsParser.html +201 -0
  32. data/docs/Cdss/Parsers/AnalysisParser.html +296 -0
  33. data/docs/Cdss/Parsers/BaseParser.html +207 -0
  34. data/docs/Cdss/Parsers/ClimateParser.html +253 -0
  35. data/docs/Cdss/Parsers/ReadingParser.html +201 -0
  36. data/docs/Cdss/Parsers/ReferenceTablesParser.html +201 -0
  37. data/docs/Cdss/Parsers/StationParser.html +201 -0
  38. data/docs/Cdss/Parsers/StructuresParser.html +305 -0
  39. data/docs/Cdss/Parsers/WaterRightsParser.html +219 -0
  40. data/docs/Cdss/Parsers/WellParser.html +357 -0
  41. data/docs/Cdss/Parsers.html +117 -0
  42. data/docs/Cdss/ReferenceTables.html +332 -0
  43. data/docs/Cdss/Structures.html +1132 -0
  44. data/docs/Cdss/SurfaceWater.html +798 -0
  45. data/docs/Cdss/Telemetry.html +763 -0
  46. data/docs/Cdss/Utils.html +1276 -0
  47. data/docs/Cdss/WaterRights.html +634 -0
  48. data/docs/Cdss.html +292 -0
  49. data/docs/_index.html +493 -0
  50. data/docs/class_list.html +54 -0
  51. data/docs/css/common.css +1 -0
  52. data/docs/css/full_list.css +58 -0
  53. data/docs/css/style.css +503 -0
  54. data/docs/file.README.html +108 -0
  55. data/docs/file_list.html +59 -0
  56. data/docs/frames.html +22 -0
  57. data/docs/index.html +108 -0
  58. data/docs/js/app.js +344 -0
  59. data/docs/js/full_list.js +242 -0
  60. data/docs/js/jquery.js +4 -0
  61. data/docs/method_list.html +790 -0
  62. data/docs/top-level-namespace.html +110 -0
  63. data/lib/cdss/admin_calls.rb +44 -0
  64. data/lib/cdss/analysis.rb +183 -0
  65. data/lib/cdss/client.rb +121 -0
  66. data/lib/cdss/climate.rb +155 -0
  67. data/lib/cdss/concerns/log_reading_attributes.rb +48 -0
  68. data/lib/cdss/concerns/well_reading_attributes.rb +56 -0
  69. data/lib/cdss/ground_water.rb +112 -0
  70. data/lib/cdss/models/admin_call.rb +45 -0
  71. data/lib/cdss/models/analysis.rb +77 -0
  72. data/lib/cdss/models/climate_station.rb +40 -0
  73. data/lib/cdss/models/reading.rb +54 -0
  74. data/lib/cdss/models/reference_table.rb +56 -0
  75. data/lib/cdss/models/station.rb +40 -0
  76. data/lib/cdss/models/structure.rb +101 -0
  77. data/lib/cdss/models/water_right.rb +47 -0
  78. data/lib/cdss/models/well.rb +43 -0
  79. data/lib/cdss/parser.rb +172 -0
  80. data/lib/cdss/parsers/admin_calls_parser.rb +47 -0
  81. data/lib/cdss/parsers/analysis_parser.rb +124 -0
  82. data/lib/cdss/parsers/base_parser.rb +18 -0
  83. data/lib/cdss/parsers/climate_parser.rb +86 -0
  84. data/lib/cdss/parsers/reading_parser.rb +90 -0
  85. data/lib/cdss/parsers/reference_tables_parser.rb +55 -0
  86. data/lib/cdss/parsers/station_parser.rb +42 -0
  87. data/lib/cdss/parsers/structures_parser.rb +96 -0
  88. data/lib/cdss/parsers/water_rights_parser.rb +77 -0
  89. data/lib/cdss/parsers/well_parser.rb +107 -0
  90. data/lib/cdss/reference_tables.rb +147 -0
  91. data/lib/cdss/structures.rb +235 -0
  92. data/lib/cdss/surface_water.rb +186 -0
  93. data/lib/cdss/telemetry.rb +98 -0
  94. data/lib/cdss/utils.rb +152 -0
  95. data/lib/cdss/version.rb +5 -0
  96. data/lib/cdss/water_rights.rb +95 -0
  97. data/lib/cdss.rb +27 -0
  98. data/sig/cdss/ruby.rbs +6 -0
  99. metadata +272 -0
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cdss
4
+ module Parsers
5
+ module StructuresParser
6
+ extend BaseParser
7
+
8
+ class << self
9
+ def parse_structures(response)
10
+ parse_collection(response) { |data| build_structure(data) }
11
+ end
12
+
13
+ def parse_diversion_records(response, type:)
14
+ parse_collection(response) { |data| build_diversion_record(data, type) }
15
+ end
16
+
17
+ def parse_water_classes(response)
18
+ parse_collection(response) { |data| build_water_class(data) }
19
+ end
20
+
21
+ private
22
+
23
+ def build_structure(data)
24
+ Models::Structure.new(
25
+ wdid: data["wdid"],
26
+ structure_name: data["structureName"],
27
+ structure_type: data["structureType"],
28
+ water_source: data["waterSource"],
29
+ division: safe_integer(data["division"]),
30
+ water_district: safe_integer(data["waterDistrict"]),
31
+ county: data["county"],
32
+ designated_basin: data["designatedBasin"],
33
+ management_district: data["managementDistrict"],
34
+ latitude: safe_float(data["latitude"]),
35
+ longitude: safe_float(data["longitude"]),
36
+ utm_x: safe_float(data["utmX"]),
37
+ utm_y: safe_float(data["utmY"]),
38
+ stream_num: data["streamNum"],
39
+ structure_num: data["structureNum"],
40
+ ciu_code: data["ciuCode"],
41
+ ciucode_desc: data["ciucodeDesc"],
42
+ modified: parse_timestamp(data["modified"]),
43
+ metadata: {}
44
+ )
45
+ end
46
+
47
+ def build_diversion_record(data, type)
48
+ Models::DiversionRecord.new(
49
+ wdid: data["wdid"],
50
+ water_class_num: safe_integer(data["waterClassNum"]),
51
+ wc_identifier: data["wcIdentifier"],
52
+ meas_interval: data["measInterval"],
53
+ meas_count: safe_integer(data["measCount"]),
54
+ data_meas_date: parse_data_meas_date(data["dataMeasDate"], type),
55
+ data_value: safe_float(data["dataValue"]),
56
+ meas_units: data["measUnits"],
57
+ obs_code: data["obsCode"],
58
+ approval_status: data["approvalStatus"],
59
+ modified: parse_timestamp(data["modified"]),
60
+ metadata: {}
61
+ )
62
+ end
63
+
64
+ def build_water_class(data)
65
+ Models::WaterClass.new(
66
+ wdid: data["wdid"],
67
+ wc_identifier: data["wcIdentifier"],
68
+ por_start: parse_timestamp(data["porStart"]),
69
+ por_end: parse_timestamp(data["porEnd"]),
70
+ div_type: data["divrectype"],
71
+ timestep: data["timestep"],
72
+ units: data["units"],
73
+ source_code: data["sourceCode"],
74
+ use_code: data["useCode"],
75
+ op_code: data["opCode"],
76
+ modified: parse_timestamp(data["modified"]),
77
+ metadata: {}
78
+ )
79
+ end
80
+
81
+ def parse_data_meas_date(date_str, type)
82
+ return nil if date_str.nil?
83
+
84
+ case type
85
+ when :year
86
+ parse_timestamp("#{date_str}-01-01 00:00:00")
87
+ when :month
88
+ parse_timestamp("#{date_str}-01 00:00:00")
89
+ else
90
+ parse_timestamp(date_str)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cdss
4
+ module Parsers
5
+ module WaterRightsParser
6
+ extend BaseParser
7
+
8
+ class << self
9
+ def parse_water_rights(response, type:)
10
+ parse_collection(response) do |data|
11
+ case type
12
+ when :net_amount
13
+ build_net_amount(data)
14
+ when :transaction
15
+ build_transaction(data)
16
+ else
17
+ raise ArgumentError, "Invalid water rights type: #{type}"
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def build_net_amount(data)
25
+ Cdss::Models::WaterRight.new(
26
+ wdid: data["wdid"],
27
+ water_right_name: data["waterRightName"],
28
+ admin_number: data["adminNumber"]&.to_f,
29
+ appropriation_date: parse_timestamp(data["appropriationDate"]),
30
+ padj_date: parse_timestamp(data["padjDate"]),
31
+ adj_type: data["adjType"],
32
+ order_number: data["orderNumber"],
33
+ prior_cases: data["priorCases"],
34
+ adj_date: parse_timestamp(data["adjDate"]),
35
+ status: data["status"],
36
+ decreed_uses: data["decreedUses"],
37
+ decreed_amount: data["decreedAmount"]&.to_f,
38
+ decreed_units: data["decreedUnits"],
39
+ county: data["county"],
40
+ water_district: data["waterDistrict"]&.to_i,
41
+ division: data["division"]&.to_i,
42
+ stream_mile: data["streamMile"]&.to_f,
43
+ structure_type: data["structureType"],
44
+ latitude: data["latitude"]&.to_f,
45
+ longitude: data["longitude"]&.to_f,
46
+ modified: parse_timestamp(data["modified"]),
47
+ metadata: {}
48
+ )
49
+ end
50
+
51
+ def build_transaction(data)
52
+ Cdss::Models::WaterRight.new(
53
+ wdid: data["wdid"],
54
+ water_right_name: data["waterRightName"],
55
+ trans_id: data["transId"],
56
+ trans_type: data["transType"],
57
+ case_number: data["caseNumber"],
58
+ adj_date: parse_timestamp(data["adjDate"]),
59
+ admin_number: data["adminNumber"]&.to_f,
60
+ order_number: data["orderNumber"],
61
+ prior_cases: data["priorCases"],
62
+ decreed_uses: data["decreedUses"],
63
+ decreed_amount: data["decreedAmount"]&.to_f,
64
+ decreed_units: data["decreedUnits"],
65
+ action_comment: data["actionComment"],
66
+ action_update: data["actionUpdate"],
67
+ county: data["county"],
68
+ water_district: data["waterDistrict"]&.to_i,
69
+ division: data["division"]&.to_i,
70
+ modified: parse_timestamp(data["modified"]),
71
+ metadata: {}
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cdss
4
+ module Parsers
5
+ module WellParser
6
+ extend BaseParser
7
+
8
+ class << self
9
+ def parse_wells(response)
10
+ parse_collection(response)
11
+ end
12
+
13
+ def parse_well_measurements(response)
14
+ parse_collection(response) { |data| build_measurement(data) }
15
+ end
16
+
17
+ def parse_geophysical_wells(response)
18
+ parse_collection(response) { |data| build_geophysical_well(data) }
19
+ end
20
+
21
+ def parse_log_picks(response)
22
+ parse_collection(response) { |data| build_log_pick(data) }
23
+ end
24
+
25
+ private
26
+
27
+ def build_resource(data)
28
+ Cdss::Models::Well.new(
29
+ well_id: data["wellId"],
30
+ well_name: data["wellName"],
31
+ latitude: data["latitude"],
32
+ longitude: data["longitude"],
33
+ location_accuracy: data["locationAccuracy"],
34
+ county: data["county"],
35
+ designated_basin: data["designatedBasin"],
36
+ management_district: data["managementDistrict"],
37
+ division: data["division"],
38
+ water_district: data["waterDistrict"],
39
+ modified: parse_timestamp(data["modified"]),
40
+ metadata: {}
41
+ )
42
+ end
43
+
44
+ def build_measurement(data)
45
+ Cdss::Models::Reading.new(
46
+ well_id: data["wellId"],
47
+ well_name: data["wellName"],
48
+ division: data["division"],
49
+ water_district: data["waterDistrict"],
50
+ county: data["county"],
51
+ management_district: data["managementDistrict"],
52
+ designated_basin: data["designatedBasin"],
53
+ publication: data["publication"],
54
+ measurement_date: parse_timestamp(data["measurementDate"]),
55
+ depth_to_water: safe_float(data["depthToWater"]),
56
+ measuring_point_above_land_surface: safe_float(data["measuringPointAboveLandSurface"]),
57
+ depth_water_below_land_surface: safe_float(data["depthWaterBelowLandSurface"]),
58
+ elevation_of_water: safe_float(data["elevationOfWater"]),
59
+ delta: safe_float(data["delta"]),
60
+ modified: parse_timestamp(data["modified"]),
61
+ data_source: data["dataSource"],
62
+ metadata: {}
63
+ )
64
+ end
65
+
66
+ def build_geophysical_well(data)
67
+ Cdss::Models::Well.new(
68
+ well_id: data["wellId"],
69
+ well_name: data["wellName"],
70
+ latitude: data["latitude"],
71
+ longitude: data["longitude"],
72
+ location_accuracy: data["locationAccuracy"],
73
+ county: data["county"],
74
+ designated_basin: data["designatedBasin"],
75
+ management_district: data["managementDistrict"],
76
+ division: data["division"],
77
+ water_district: data["waterDistrict"],
78
+ depth: safe_float(data["totalDepth"]),
79
+ elevation: safe_float(data["groundElevation"]),
80
+ modified: parse_timestamp(data["modified"]),
81
+ metadata: {}
82
+ )
83
+ end
84
+
85
+ def build_log_pick(data)
86
+ Cdss::Models::Reading.new(
87
+ well_id: data["wellId"],
88
+ pick_depth: safe_float(data["pickDepth"]),
89
+ formation: data["formation"],
90
+ member: data["member"],
91
+ pick_quality: data["pickQuality"],
92
+ comments: data["comments"],
93
+ modified: parse_timestamp(data["modified"]),
94
+ aquifer: data["aquifer"],
95
+ g_log_top_depth: safe_float(data["gLogTopDepth"]),
96
+ g_log_base_depth: safe_float(data["gLogBaseDepth"]),
97
+ g_log_top_elev: safe_float(data["gLogTopElev"]),
98
+ g_log_base_elev: safe_float(data["gLogBaseElev"]),
99
+ g_log_thickness: safe_float(data["gLogThickness"]),
100
+ comment: data["comment"],
101
+ metadata: {}
102
+ )
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cdss
4
+ # Provides access to reference tables from the CDSS API.
5
+ # These tables provide metadata and lookup information for various CDSS resources.
6
+ module ReferenceTables
7
+ include Utils
8
+
9
+ # List of valid reference table names that can be queried
10
+ VALID_TABLES = %w[
11
+ county
12
+ waterdistricts
13
+ waterdivisions
14
+ designatedbasins
15
+ managementdistricts
16
+ telemetryparams
17
+ climateparams
18
+ divrectypes
19
+ flags
20
+ ].freeze
21
+
22
+ # Fetches reference table data from the CDSS API.
23
+ #
24
+ # @param [String] table_name The name of the reference table to fetch.
25
+ # Must be one of: county, waterdistricts, waterdivisions, designatedbasins,
26
+ # managementdistricts, telemetryparams, climateparams, divrectypes, flags
27
+ # @param [Hash] params Additional parameters for filtering table data
28
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of reference table records
29
+ # @raise [ArgumentError] If an invalid table name is provided
30
+ def get_reference_table(table_name, **params)
31
+ validate_table_name!(table_name)
32
+ method_name = "fetch_#{table_name}_reference"
33
+ send(method_name, **params)
34
+ end
35
+
36
+ private
37
+
38
+ # Validates that the requested table name is supported.
39
+ #
40
+ # @param [String] table_name Name of table to validate
41
+ # @raise [ArgumentError] If table_name is not in VALID_TABLES list
42
+ def validate_table_name!(table_name)
43
+ return if VALID_TABLES.include?(table_name)
44
+
45
+ raise ArgumentError,
46
+ "Invalid table_name: #{table_name}. Valid values are: #{VALID_TABLES.join(', ')}"
47
+ end
48
+
49
+ # Fetches county reference data.
50
+ #
51
+ # @param [String, nil] county County name to filter by
52
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of county records
53
+ def fetch_county_reference(county: nil)
54
+ query = build_query({ county: county })
55
+ fetch_reference_data("/referencetables/county/", query)
56
+ end
57
+
58
+ # Fetches water district reference data.
59
+ #
60
+ # @param [Integer, nil] division Division to filter by
61
+ # @param [Integer, nil] water_district Water district to filter by
62
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of water district records
63
+ def fetch_waterdistricts_reference(division: nil, water_district: nil)
64
+ query = build_query(
65
+ {
66
+ division: division,
67
+ waterDistrict: water_district
68
+ }
69
+ )
70
+ fetch_reference_data("/referencetables/waterdistrict/", query)
71
+ end
72
+
73
+ # Fetches water division reference data.
74
+ #
75
+ # @param [Integer, nil] division Division to filter by
76
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of water division records
77
+ def fetch_waterdivisions_reference(division: nil)
78
+ query = build_query({ division: division })
79
+ fetch_reference_data("/referencetables/waterdivision/", query)
80
+ end
81
+
82
+ # Fetches management district reference data.
83
+ #
84
+ # @param [String, nil] management_district Management district name to filter by
85
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of management district records
86
+ def fetch_managementdistricts_reference(management_district: nil)
87
+ query = build_query({ managementDistrictName: management_district })
88
+ fetch_reference_data("/referencetables/managementdistrict/", query)
89
+ end
90
+
91
+ # Fetches designated basin reference data.
92
+ #
93
+ # @param [String, nil] designated_basin Designated basin name to filter by
94
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of designated basin records
95
+ def fetch_designatedbasins_reference(designated_basin: nil)
96
+ query = build_query({ designatedBasinName: designated_basin })
97
+ fetch_reference_data("/referencetables/designatedbasin/", query)
98
+ end
99
+
100
+ # Fetches telemetry parameter reference data.
101
+ #
102
+ # @param [String, nil] param Parameter name to filter by
103
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of telemetry parameter records
104
+ def fetch_telemetryparams_reference(param: nil)
105
+ query = build_query({ parameter: param })
106
+ fetch_reference_data("/referencetables/telemetryparams/", query)
107
+ end
108
+
109
+ # Fetches climate parameter reference data.
110
+ #
111
+ # @param [String, nil] param Climate parameter type to filter by
112
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of climate parameter records
113
+ def fetch_climateparams_reference(param: nil)
114
+ query = build_query({ measType: param })
115
+ fetch_reference_data("/referencetables/climatestationmeastype/", query)
116
+ end
117
+
118
+ # Fetches diversion record type reference data.
119
+ #
120
+ # @param [String, nil] divrectype Record type to filter by
121
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of diversion record type records
122
+ def fetch_divrectypes_reference(divrectype: nil)
123
+ query = build_query({ divRecType: divrectype })
124
+ fetch_reference_data("/referencetables/divrectypes/", query)
125
+ end
126
+
127
+ # Fetches station flag reference data.
128
+ #
129
+ # @param [String, nil] flag Flag to filter by
130
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of station flag records
131
+ def fetch_flags_reference(flag: nil)
132
+ query = build_query({ flag: flag })
133
+ fetch_reference_data("/referencetables/stationflags/", query)
134
+ end
135
+
136
+ # Fetches and parses reference table data from an endpoint.
137
+ #
138
+ # @param [String] endpoint API endpoint path
139
+ # @param [Hash] query Query parameters for the request
140
+ # @return [Array<Cdss::Models::ReferenceTable>] Array of reference table records
141
+ def fetch_reference_data(endpoint, query)
142
+ fetch_paginated_data(endpoint: endpoint, query: query) do |data|
143
+ Parser.parse_reference_table(data)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cdss
4
+ # Provides methods for accessing water structures data from the CDSS API.
5
+ #
6
+ # This module includes functionality for retrieving water structures,
7
+ # diversion records, stage/volume data, and water classes.
8
+ module Structures
9
+ include Utils
10
+
11
+ # Fetches a list of administrative structures based on various filtering criteria.
12
+ #
13
+ # @param [Hash, Array, nil] aoi Area of interest for spatial searches. If hash, must contain :latitude and :longitude keys.
14
+ # @param [Integer, nil] radius Radius in miles for spatial search around aoi. Defaults to 20 if aoi is provided.
15
+ # @param [String, nil] county County to filter structures.
16
+ # @param [Integer, nil] division Water division to filter structures.
17
+ # @param [String, nil] gnis_id GNIS ID to filter structures.
18
+ # @param [Integer, nil] water_district Water district to filter structures.
19
+ # @param [String, Array<String>, nil] wdid WDID code(s) to filter specific structures.
20
+ # @return [Array<Models::Structure>] Array of matching structures.
21
+ # @raise [ArgumentError] If aoi parameter is provided but invalid.
22
+ def get_structures(aoi: nil, radius: nil, county: nil, division: nil, gnis_id: nil, water_district: nil, wdid: nil)
23
+ query = build_query({
24
+ county: county,
25
+ division: division,
26
+ gnisId: gnis_id,
27
+ waterDistrict: water_district,
28
+ wdid: Array(wdid).join("%2C+"),
29
+ units: "miles"
30
+ })
31
+
32
+ if aoi
33
+ coords = process_aoi(aoi)
34
+ query.merge!({
35
+ latitude: coords[:lat],
36
+ longitude: coords[:lng],
37
+ radius: radius || 20
38
+ })
39
+ end
40
+
41
+ fetch_paginated_data(
42
+ endpoint: "/structures/",
43
+ query: query
44
+ ) do |data|
45
+ Parser.parse_structures(data)
46
+ end
47
+ end
48
+
49
+ # Fetches diversion records time series data.
50
+ #
51
+ # @param [String, Array<String>] wdid WDID code(s) of structures.
52
+ # @param [String, nil] wc_identifier Water class identifier.
53
+ # @param [Date, nil] start_date Start date for time series data.
54
+ # @param [Date, nil] end_date End date for time series data.
55
+ # @param [String, nil] timescale Time interval ('day', 'month', or 'year'). Defaults to 'day'.
56
+ # @return [Array<Models::DiversionRecord>] Array of diversion records.
57
+ # @raise [ArgumentError] If an invalid timescale is provided.
58
+ def get_diversion_records_ts(wdid:, wc_identifier: nil, start_date: nil, end_date: nil, timescale: "day")
59
+ validate_timescale!(timescale)
60
+
61
+ method_name = "fetch_diversion_records_#{timescale}"
62
+ send(method_name, wdid: wdid, wc_identifier: wc_identifier, start_date: start_date, end_date: end_date)
63
+ end
64
+
65
+ # Fetches stage/volume record data.
66
+ #
67
+ # @param [String] wdid WDID code of structure.
68
+ # @param [Date, nil] start_date Start date for records.
69
+ # @param [Date, nil] end_date End date for records.
70
+ # @return [Array<Models::DiversionRecord>] Array of stage/volume records.
71
+ def get_stage_volume_ts(wdid:, start_date: nil, end_date: nil)
72
+ query = build_query({
73
+ wdid: wdid,
74
+ "min-dataMeasDate": format_date(start_date),
75
+ "max-dataMeasDate": format_date(end_date)
76
+ })
77
+
78
+ fetch_paginated_data(
79
+ endpoint: "/structures/divrec/stagevolume/",
80
+ query: query
81
+ ) do |data|
82
+ Parser.parse_diversion_records(data, type: :stage_volume)
83
+ end
84
+ end
85
+
86
+ # Fetches water classes for structures.
87
+ #
88
+ # @param [Hash] params Query parameters including wdid, county, division, etc.
89
+ # @option params [String, Array<String>] :wdid WDID code(s) to filter by
90
+ # @option params [String] :county County name to filter by
91
+ # @option params [Integer] :division Division number to filter by
92
+ # @option params [Integer] :water_district Water district to filter by
93
+ # @option params [String] :wc_identifier Water class identifier
94
+ # @option params [String] :timestep Time step for records
95
+ # @option params [Date] :start_date Start date for records
96
+ # @option params [Date] :end_date End date for records
97
+ # @option params [String] :divrectype Diversion record type
98
+ # @option params [String] :ciu_code CIU code
99
+ # @option params [String] :gnis_id GNIS ID
100
+ # @option params [Hash, Array] :aoi Area of interest for spatial search
101
+ # @option params [Integer] :radius Radius in miles for spatial search
102
+ # @return [Array<Models::WaterClass>] Array of water classes.
103
+ def get_water_classes(**params)
104
+ query = build_query({
105
+ wdid: Array(params[:wdid]).join("%2C+"),
106
+ county: params[:county],
107
+ division: params[:division],
108
+ waterDistrict: params[:water_district],
109
+ wcIdentifier: format_wc_identifier(params[:wc_identifier]),
110
+ timestep: params[:timestep],
111
+ "min-porStart": format_date(params[:start_date]),
112
+ "min-porEnd": format_date(params[:end_date]),
113
+ divrectype: params[:divrectype],
114
+ ciuCode: params[:ciu_code],
115
+ gnisId: params[:gnis_id]
116
+ })
117
+
118
+ if params[:aoi]
119
+ coords = process_aoi(params[:aoi])
120
+ query.merge!({
121
+ latitude: coords[:lat],
122
+ longitude: coords[:lng],
123
+ radius: params[:radius] || 20,
124
+ units: "miles"
125
+ })
126
+ end
127
+
128
+ fetch_paginated_data(
129
+ endpoint: "/structures/divrec/waterclasses/",
130
+ query: query
131
+ ) do |data|
132
+ Parser.parse_water_classes(data)
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ # Validates the provided timescale against allowed values.
139
+ #
140
+ # @param [String] timescale The timescale to validate
141
+ # @raise [ArgumentError] If timescale is not a valid value
142
+ def validate_timescale!(timescale)
143
+ valid_timescales = {
144
+ "day" => %w[day days daily d],
145
+ "month" => %w[month months monthly mon m],
146
+ "year" => %w[year years yearly annual annually yr y]
147
+ }
148
+
149
+ normalized = timescale.to_s.downcase
150
+ return if valid_timescales.values.flatten.include?(normalized)
151
+
152
+ raise ArgumentError,
153
+ "Invalid timescale: #{timescale}. Valid values are: #{valid_timescales.values.flatten.join(', ')}"
154
+ end
155
+
156
+ # Formats a water class identifier for API queries.
157
+ #
158
+ # @param [String, nil] identifier The identifier to format
159
+ # @return [String] Formatted identifier for API use
160
+ def format_wc_identifier(identifier)
161
+ return "*diversion*" if identifier.nil?
162
+ return "diversion" if %w[diversion diversions div divs d].include?(identifier.downcase)
163
+ return "release" if %w[release releases rel rels r].include?(identifier.downcase)
164
+
165
+ "*#{identifier}*"
166
+ end
167
+
168
+ # Fetches daily diversion records.
169
+ #
170
+ # @param [String, Array<String>] wdid WDID code(s)
171
+ # @param [String] wc_identifier Water class identifier
172
+ # @param [Date, nil] start_date Start date for records
173
+ # @param [Date, nil] end_date End date for records
174
+ # @return [Array<Models::DiversionRecord>] Array of daily diversion records
175
+ def fetch_diversion_records_day(wdid:, wc_identifier:, start_date:, end_date:)
176
+ query = build_diversion_query(wdid, wc_identifier, start_date, end_date)
177
+ fetch_paginated_data(
178
+ endpoint: "/structures/divrec/divrecday/",
179
+ query: query
180
+ ) do |data|
181
+ Parser.parse_diversion_records(data, type: :day)
182
+ end
183
+ end
184
+
185
+ # Fetches monthly diversion records.
186
+ #
187
+ # @param [String, Array<String>] wdid WDID code(s)
188
+ # @param [String] wc_identifier Water class identifier
189
+ # @param [Date, nil] start_date Start date for records
190
+ # @param [Date, nil] end_date End date for records
191
+ # @return [Array<Models::DiversionRecord>] Array of monthly diversion records
192
+ def fetch_diversion_records_month(wdid:, wc_identifier:, start_date:, end_date:)
193
+ query = build_diversion_query(wdid, wc_identifier, start_date, end_date)
194
+ fetch_paginated_data(
195
+ endpoint: "/structures/divrec/divrecmonth/",
196
+ query: query
197
+ ) do |data|
198
+ Parser.parse_diversion_records(data, type: :month)
199
+ end
200
+ end
201
+
202
+ # Fetches yearly diversion records.
203
+ #
204
+ # @param [String, Array<String>] wdid WDID code(s)
205
+ # @param [String] wc_identifier Water class identifier
206
+ # @param [Date, nil] start_date Start date for records
207
+ # @param [Date, nil] end_date End date for records
208
+ # @return [Array<Models::DiversionRecord>] Array of yearly diversion records
209
+ def fetch_diversion_records_year(wdid:, wc_identifier:, start_date:, end_date:)
210
+ query = build_diversion_query(wdid, wc_identifier, start_date, end_date)
211
+ fetch_paginated_data(
212
+ endpoint: "/structures/divrec/divrecyear/",
213
+ query: query
214
+ ) do |data|
215
+ Parser.parse_diversion_records(data, type: :year)
216
+ end
217
+ end
218
+
219
+ # Builds a query for diversion record requests.
220
+ #
221
+ # @param [String, Array<String>] wdid WDID code(s)
222
+ # @param [String] wc_identifier Water class identifier
223
+ # @param [Date, nil] start_date Start date for records
224
+ # @param [Date, nil] end_date End date for records
225
+ # @return [Hash] Query parameters for the API request
226
+ def build_diversion_query(wdid, wc_identifier, start_date, end_date)
227
+ build_query({
228
+ wdid: Array(wdid).join("%2C+"),
229
+ wcIdentifier: format_wc_identifier(wc_identifier),
230
+ "min-dataMeasDate": format_date(start_date),
231
+ "max-dataMeasDate": format_date(end_date)
232
+ })
233
+ end
234
+ end
235
+ end