afscgap.convert
Logic to convert types when interacting with the AFSC GAP REST service.
(c) 2023 Regents of University of California / The Eric and Wendy Schmidt Center for Data Science and the Environment at UC Berkeley.
This file is part of afscgap released under the BSD 3-Clause License. See LICENSE.md.
1""" 2Logic to convert types when interacting with the AFSC GAP REST service. 3 4(c) 2023 Regents of University of California / The Eric and Wendy Schmidt Center 5for Data Science and the Environment at UC Berkeley. 6 7This file is part of afscgap released under the BSD 3-Clause License. See 8LICENSE.md. 9""" 10import re 11 12from afscgap.typesdef import FLOAT_PARAM 13from afscgap.typesdef import OPT_FLOAT 14from afscgap.typesdef import STR_PARAM 15 16DATE_REGEX = re.compile('(?P<month>\\d{2})\\/(?P<day>\\d{2})\\/' + \ 17 '(?P<year>\\d{4}) (?P<hours>\\d{2})\\:(?P<minutes>\\d{2})\\:' + \ 18 '(?P<seconds>\\d{2})') 19DATE_TEMPLATE = '%s/%s/%s %s:%s:%s' 20ISO_8601_REGEX = re.compile('(?P<year>\\d{4})\\-(?P<month>\\d{2})\\-' + \ 21 '(?P<day>\\d{2})T(?P<hours>\\d{2})\\:(?P<minutes>\\d{2})\\:' + \ 22 '(?P<seconds>\\d{2})') 23ISO_8601_TEMPLATE = '%s-%s-%sT%s:%s:%s' 24 25AREA_CONVERTERS = { 26 'ha': lambda x: x, 27 'm2': lambda x: x * 10000, 28 'km2': lambda x: x * 0.01 29} 30 31AREA_UNCONVERTERS = { 32 'ha': lambda x: x, 33 'm2': lambda x: x / 10000, 34 'km2': lambda x: x / 0.01 35} 36 37DISTANCE_CONVERTERS = { 38 'm': lambda x: x, 39 'km': lambda x: x / 1000 40} 41 42DISTANCE_UNCONVERTERS = { 43 'm': lambda x: x, 44 'km': lambda x: x * 1000 45} 46 47TEMPERATURE_CONVERTERS = { 48 'c': lambda x: x, 49 'f': lambda x: x * 9 / 5 + 32 50} 51 52TEMPERATURE_UNCONVERTERS = { 53 'c': lambda x: x, 54 'f': lambda x: (x - 32) * 5 / 9 55} 56 57TIME_CONVERTERS = { 58 'day': lambda x: x / 24, 59 'hr': lambda x: x, 60 'min': lambda x: x * 60 61} 62 63TIME_UNCONVERTERS = { 64 'day': lambda x: x * 24, 65 'hr': lambda x: x, 66 'min': lambda x: x / 60 67} 68 69WEIGHT_CONVERTERS = { 70 'g': lambda x: x * 1000, 71 'kg': lambda x: x 72} 73 74WEIGHT_UNCONVERTERS = { 75 'g': lambda x: x / 1000, 76 'kg': lambda x: x 77} 78 79 80def convert_from_iso8601(target: STR_PARAM) -> STR_PARAM: 81 """Convert strings from ISO 8601 format to API format. 82 83 Args: 84 target: The string or dictionary in which to perform the 85 transformations. 86 87 Returns: 88 If given an ISO 8601 string, will convert from ISO 8601 to the API 89 datetime string format. Similarly, if given a dictionary, all values 90 matching an ISO 8601 string will be converted to the API datetime string 91 format. If given None, returns None. 92 """ 93 if target is None: 94 return None 95 elif isinstance(target, str): 96 return convert_from_iso8601_str(target) 97 elif isinstance(target, dict): 98 items = target.items() 99 output_dict = {} 100 101 for key, value in items: 102 if isinstance(value, str): 103 output_dict[key] = convert_from_iso8601_str(value) 104 else: 105 output_dict[key] = value 106 107 return output_dict 108 else: 109 return target 110 111 112def convert_from_iso8601_str(target: str) -> str: 113 """Attempt converting an ISO 8601 string to an API-provided datetime. 114 115 Args: 116 target: The datetime string to try to interpret. 117 118 Returns: 119 The datetime input string as a ISO 8601 string or the original value of 120 target if it could not be parsed. 121 """ 122 match = ISO_8601_REGEX.match(target) 123 124 if not match: 125 return target 126 127 year = match.group('year') 128 month = match.group('month') 129 day = match.group('day') 130 hours = match.group('hours') 131 minutes = match.group('minutes') 132 seconds = match.group('seconds') 133 134 return DATE_TEMPLATE % (month, day, year, hours, minutes, seconds) 135 136 137def convert_to_iso8601(target: str) -> str: 138 """Attempt converting an API-provided datetime to ISO 8601. 139 140 Args: 141 target: The datetime string to try to interpret. 142 Returns: 143 The datetime input string as a ISO 8601 string or the original value of 144 target if it could not be parsed. 145 """ 146 match = DATE_REGEX.match(target) 147 148 if not match: 149 return target 150 151 year = match.group('year') 152 month = match.group('month') 153 day = match.group('day') 154 hours = match.group('hours') 155 minutes = match.group('minutes') 156 seconds = match.group('seconds') 157 158 return ISO_8601_TEMPLATE % (year, month, day, hours, minutes, seconds) 159 160 161def is_iso8601(target: str) -> bool: 162 """Determine if a string matches an expected ISO 8601 format. 163 164 Args: 165 target: The string to test. 166 167 Returns: 168 True if it matches the expected format and false otherwise. 169 """ 170 return ISO_8601_REGEX.match(target) is not None 171 172 173def convert_area(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 174 """Convert an area. 175 176 Args: 177 target: The value to convert in hectares. 178 units: Desired units. 179 180 Returns: 181 The converted value. Note that, if target is None, will return None. 182 """ 183 if target is None: 184 return None 185 186 return AREA_CONVERTERS[units](target) 187 188 189def unconvert_area(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 190 """Standardize an area to the API-native units (hectare). 191 192 Args: 193 target: The value to convert in hectares. 194 units: The units of value. 195 196 Returns: 197 The converted value. Note that, if target is None, will return None. 198 """ 199 if target is None: 200 return None 201 202 if isinstance(target, dict): 203 return target 204 205 return AREA_UNCONVERTERS[units](target) 206 207 208def convert_degrees(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 209 """Convert targets from degrees to another units. 210 211 Args: 212 target: The value to convert which may be None. 213 units: Desired units. 214 215 Returns: 216 The same value input after asserting that units are dd, the only 217 supported units. 218 """ 219 assert units == 'dd' 220 return target 221 222 223def unconvert_degrees(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 224 """Standardize a degree to the API-native units (degrees). 225 226 Args: 227 target: The value to convert which may be None. 228 units: The units of value. 229 230 Returns: 231 The same value input after asserting that units are dd, the only 232 supported units. 233 """ 234 assert units == 'dd' 235 return target 236 237 238def convert_distance(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 239 """Convert a linear distance. 240 241 Args: 242 target: The value to convert in meters. 243 units: Desired units. 244 245 Returns: 246 The converted value. Note that, if target is None, will return None. 247 """ 248 if target is None: 249 return None 250 251 return DISTANCE_CONVERTERS[units](target) 252 253 254def unconvert_distance(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 255 """Convert a linear distance to the API-native units (meters). 256 257 Args: 258 target: The value to convert in meters. 259 units: The units of value. 260 261 Returns: 262 The converted value. Note that, if target is None, will return None. 263 """ 264 if target is None: 265 return None 266 267 if isinstance(target, dict): 268 return target 269 270 return DISTANCE_UNCONVERTERS[units](target) 271 272 273def convert_temperature(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 274 """Convert a temperature. 275 276 Args: 277 target: The value to convert in Celcius. 278 units: Desired units. 279 280 Returns: 281 The converted value. Note that, if target is None, will return None. 282 """ 283 if target is None: 284 return None 285 286 return TEMPERATURE_CONVERTERS[units](target) 287 288 289def unconvert_temperature(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 290 """Convert a linear temperature to the API-native units (Celsius). 291 292 Args: 293 target: The value to convert in Celcius. 294 units: The units of value. 295 296 Returns: 297 The converted value. Note that, if target is None, will return None. 298 """ 299 if target is None: 300 return None 301 302 if isinstance(target, dict): 303 return target 304 305 return TEMPERATURE_UNCONVERTERS[units](target) 306 307 308def convert_time(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 309 """Convert a time. 310 311 Args: 312 target: The value to convert in hours. 313 units: Desired units. 314 315 Returns: 316 The converted value. Note that, if target is None, will return None. 317 """ 318 if target is None: 319 return None 320 321 return TIME_CONVERTERS[units](target) 322 323 324def unconvert_time(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 325 """Convert a time to the API-native units (hours). 326 327 Args: 328 target: The value to convert in hours. 329 units: The units of value. 330 331 Returns: 332 The converted value. Note that, if target is None, will return None. 333 """ 334 if target is None: 335 return None 336 337 if isinstance(target, dict): 338 return target 339 340 return TIME_UNCONVERTERS[units](target) 341 342 343def convert_weight(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 344 """Convert a weight. 345 346 Args: 347 target: The value to convert in kilograms. 348 units: Desired units. 349 350 Returns: 351 The converted value. Note that, if target is None, will return None. 352 """ 353 if target is None: 354 return None 355 356 return WEIGHT_CONVERTERS[units](target) 357 358 359def unconvert_weight(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 360 """Convert a weight to the API-native units (kilograms). 361 362 Args: 363 target: The value to convert in kilograms. 364 units: The units of value. 365 366 Returns: 367 The converted value. Note that, if target is None, will return None. 368 """ 369 if target is None: 370 return None 371 372 if isinstance(target, dict): 373 return target 374 375 return WEIGHT_UNCONVERTERS[units](target)
81def convert_from_iso8601(target: STR_PARAM) -> STR_PARAM: 82 """Convert strings from ISO 8601 format to API format. 83 84 Args: 85 target: The string or dictionary in which to perform the 86 transformations. 87 88 Returns: 89 If given an ISO 8601 string, will convert from ISO 8601 to the API 90 datetime string format. Similarly, if given a dictionary, all values 91 matching an ISO 8601 string will be converted to the API datetime string 92 format. If given None, returns None. 93 """ 94 if target is None: 95 return None 96 elif isinstance(target, str): 97 return convert_from_iso8601_str(target) 98 elif isinstance(target, dict): 99 items = target.items() 100 output_dict = {} 101 102 for key, value in items: 103 if isinstance(value, str): 104 output_dict[key] = convert_from_iso8601_str(value) 105 else: 106 output_dict[key] = value 107 108 return output_dict 109 else: 110 return target
Convert strings from ISO 8601 format to API format.
Arguments:
- target: The string or dictionary in which to perform the transformations.
Returns:
If given an ISO 8601 string, will convert from ISO 8601 to the API datetime string format. Similarly, if given a dictionary, all values matching an ISO 8601 string will be converted to the API datetime string format. If given None, returns None.
113def convert_from_iso8601_str(target: str) -> str: 114 """Attempt converting an ISO 8601 string to an API-provided datetime. 115 116 Args: 117 target: The datetime string to try to interpret. 118 119 Returns: 120 The datetime input string as a ISO 8601 string or the original value of 121 target if it could not be parsed. 122 """ 123 match = ISO_8601_REGEX.match(target) 124 125 if not match: 126 return target 127 128 year = match.group('year') 129 month = match.group('month') 130 day = match.group('day') 131 hours = match.group('hours') 132 minutes = match.group('minutes') 133 seconds = match.group('seconds') 134 135 return DATE_TEMPLATE % (month, day, year, hours, minutes, seconds)
Attempt converting an ISO 8601 string to an API-provided datetime.
Arguments:
- target: The datetime string to try to interpret.
Returns:
The datetime input string as a ISO 8601 string or the original value of target if it could not be parsed.
138def convert_to_iso8601(target: str) -> str: 139 """Attempt converting an API-provided datetime to ISO 8601. 140 141 Args: 142 target: The datetime string to try to interpret. 143 Returns: 144 The datetime input string as a ISO 8601 string or the original value of 145 target if it could not be parsed. 146 """ 147 match = DATE_REGEX.match(target) 148 149 if not match: 150 return target 151 152 year = match.group('year') 153 month = match.group('month') 154 day = match.group('day') 155 hours = match.group('hours') 156 minutes = match.group('minutes') 157 seconds = match.group('seconds') 158 159 return ISO_8601_TEMPLATE % (year, month, day, hours, minutes, seconds)
Attempt converting an API-provided datetime to ISO 8601.
Arguments:
- target: The datetime string to try to interpret.
Returns:
The datetime input string as a ISO 8601 string or the original value of target if it could not be parsed.
162def is_iso8601(target: str) -> bool: 163 """Determine if a string matches an expected ISO 8601 format. 164 165 Args: 166 target: The string to test. 167 168 Returns: 169 True if it matches the expected format and false otherwise. 170 """ 171 return ISO_8601_REGEX.match(target) is not None
Determine if a string matches an expected ISO 8601 format.
Arguments:
- target: The string to test.
Returns:
True if it matches the expected format and false otherwise.
174def convert_area(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 175 """Convert an area. 176 177 Args: 178 target: The value to convert in hectares. 179 units: Desired units. 180 181 Returns: 182 The converted value. Note that, if target is None, will return None. 183 """ 184 if target is None: 185 return None 186 187 return AREA_CONVERTERS[units](target)
Convert an area.
Arguments:
- target: The value to convert in hectares.
- units: Desired units.
Returns:
The converted value. Note that, if target is None, will return None.
190def unconvert_area(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 191 """Standardize an area to the API-native units (hectare). 192 193 Args: 194 target: The value to convert in hectares. 195 units: The units of value. 196 197 Returns: 198 The converted value. Note that, if target is None, will return None. 199 """ 200 if target is None: 201 return None 202 203 if isinstance(target, dict): 204 return target 205 206 return AREA_UNCONVERTERS[units](target)
Standardize an area to the API-native units (hectare).
Arguments:
- target: The value to convert in hectares.
- units: The units of value.
Returns:
The converted value. Note that, if target is None, will return None.
209def convert_degrees(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 210 """Convert targets from degrees to another units. 211 212 Args: 213 target: The value to convert which may be None. 214 units: Desired units. 215 216 Returns: 217 The same value input after asserting that units are dd, the only 218 supported units. 219 """ 220 assert units == 'dd' 221 return target
Convert targets from degrees to another units.
Arguments:
- target: The value to convert which may be None.
- units: Desired units.
Returns:
The same value input after asserting that units are dd, the only supported units.
224def unconvert_degrees(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 225 """Standardize a degree to the API-native units (degrees). 226 227 Args: 228 target: The value to convert which may be None. 229 units: The units of value. 230 231 Returns: 232 The same value input after asserting that units are dd, the only 233 supported units. 234 """ 235 assert units == 'dd' 236 return target
Standardize a degree to the API-native units (degrees).
Arguments:
- target: The value to convert which may be None.
- units: The units of value.
Returns:
The same value input after asserting that units are dd, the only supported units.
239def convert_distance(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 240 """Convert a linear distance. 241 242 Args: 243 target: The value to convert in meters. 244 units: Desired units. 245 246 Returns: 247 The converted value. Note that, if target is None, will return None. 248 """ 249 if target is None: 250 return None 251 252 return DISTANCE_CONVERTERS[units](target)
Convert a linear distance.
Arguments:
- target: The value to convert in meters.
- units: Desired units.
Returns:
The converted value. Note that, if target is None, will return None.
255def unconvert_distance(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 256 """Convert a linear distance to the API-native units (meters). 257 258 Args: 259 target: The value to convert in meters. 260 units: The units of value. 261 262 Returns: 263 The converted value. Note that, if target is None, will return None. 264 """ 265 if target is None: 266 return None 267 268 if isinstance(target, dict): 269 return target 270 271 return DISTANCE_UNCONVERTERS[units](target)
Convert a linear distance to the API-native units (meters).
Arguments:
- target: The value to convert in meters.
- units: The units of value.
Returns:
The converted value. Note that, if target is None, will return None.
274def convert_temperature(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 275 """Convert a temperature. 276 277 Args: 278 target: The value to convert in Celcius. 279 units: Desired units. 280 281 Returns: 282 The converted value. Note that, if target is None, will return None. 283 """ 284 if target is None: 285 return None 286 287 return TEMPERATURE_CONVERTERS[units](target)
Convert a temperature.
Arguments:
- target: The value to convert in Celcius.
- units: Desired units.
Returns:
The converted value. Note that, if target is None, will return None.
290def unconvert_temperature(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 291 """Convert a linear temperature to the API-native units (Celsius). 292 293 Args: 294 target: The value to convert in Celcius. 295 units: The units of value. 296 297 Returns: 298 The converted value. Note that, if target is None, will return None. 299 """ 300 if target is None: 301 return None 302 303 if isinstance(target, dict): 304 return target 305 306 return TEMPERATURE_UNCONVERTERS[units](target)
Convert a linear temperature to the API-native units (Celsius).
Arguments:
- target: The value to convert in Celcius.
- units: The units of value.
Returns:
The converted value. Note that, if target is None, will return None.
309def convert_time(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 310 """Convert a time. 311 312 Args: 313 target: The value to convert in hours. 314 units: Desired units. 315 316 Returns: 317 The converted value. Note that, if target is None, will return None. 318 """ 319 if target is None: 320 return None 321 322 return TIME_CONVERTERS[units](target)
Convert a time.
Arguments:
- target: The value to convert in hours.
- units: Desired units.
Returns:
The converted value. Note that, if target is None, will return None.
325def unconvert_time(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 326 """Convert a time to the API-native units (hours). 327 328 Args: 329 target: The value to convert in hours. 330 units: The units of value. 331 332 Returns: 333 The converted value. Note that, if target is None, will return None. 334 """ 335 if target is None: 336 return None 337 338 if isinstance(target, dict): 339 return target 340 341 return TIME_UNCONVERTERS[units](target)
Convert a time to the API-native units (hours).
Arguments:
- target: The value to convert in hours.
- units: The units of value.
Returns:
The converted value. Note that, if target is None, will return None.
344def convert_weight(target: OPT_FLOAT, units: str) -> OPT_FLOAT: 345 """Convert a weight. 346 347 Args: 348 target: The value to convert in kilograms. 349 units: Desired units. 350 351 Returns: 352 The converted value. Note that, if target is None, will return None. 353 """ 354 if target is None: 355 return None 356 357 return WEIGHT_CONVERTERS[units](target)
Convert a weight.
Arguments:
- target: The value to convert in kilograms.
- units: Desired units.
Returns:
The converted value. Note that, if target is None, will return None.
360def unconvert_weight(target: FLOAT_PARAM, units: str) -> FLOAT_PARAM: 361 """Convert a weight to the API-native units (kilograms). 362 363 Args: 364 target: The value to convert in kilograms. 365 units: The units of value. 366 367 Returns: 368 The converted value. Note that, if target is None, will return None. 369 """ 370 if target is None: 371 return None 372 373 if isinstance(target, dict): 374 return target 375 376 return WEIGHT_UNCONVERTERS[units](target)
Convert a weight to the API-native units (kilograms).
Arguments:
- target: The value to convert in kilograms.
- units: The units of value.
Returns:
The converted value. Note that, if target is None, will return None.