def get_subset_limits(subsetlimits, parser): """ :param subsetlimits: List of subset limit strings :param parser: The parser used to report errors :return: The parsed datagroups as a list of dictionaries """ from cis.parse_datetime import parse_datetime, parse_as_number_or_datetime from cis.subsetting.subset_limits import SubsetLimits # Split into the limits for each dimension. split_input = split_outside_brackets(subsetlimits) if len(split_input) == 0: parser.error("Limits for at least one dimension must be specified for subsetting") limit_dict = {} for seg in split_input: # Parse out dimension name and limit value strings; the expected format is: # <dim_name>=[<start_value>,<end_value>] # or # <dim_name>=[<start_value>] match = re.match(r'(?P<dim>[^=]+)=\[(?P<start>[^],]+)(?:,(?P<end>[^],]+))?\]$', seg) if match is None or match.group('dim') is None or match.group('start') is None: parser.error( "A dimension for subsetting does not have dimension name, start value and/or end value specified") else: dim_name = match.group('dim') limit1 = match.group('start') if match.group('end') is None: limit2 = limit1 else: limit2 = match.group('end') # If the dimension is specified as x, y, z, or t, assume that the dimension is spatial or temporal in the # obvious way. Otherwise, parse what is found as a date/time or number. is_time = None if dim_name.lower() == 't': limit1_parsed = parse_datetime(limit1, 'subset range start date/time', parser) limit2_parsed = parse_datetime(limit2, 'subset range end date/time', parser) is_time = True elif dim_name.lower() in ['x', 'y', 'z']: limit1_parsed = parse_float(limit1, 'subset range start coordinate', parser) limit2_parsed = parse_float(limit2, 'subset range start coordinate', parser) is_time = False if dim_name.lower() == 'x': if not limit1_parsed <= limit2_parsed: parser.error("Longitude limits must be monotonically increasing (i.e. for x[A,B] A <= B). For " "example, x=[90,-90] is invalid but x=[90,270] is valid") if not limit2_parsed - limit1_parsed <= 360: parser.error("Longitude limits should not be more than 360 degrees apart " "(i.e. for x[A,B] B-A <= 360)") else: limit1_parsed = parse_as_number_or_datetime(limit1, 'subset range start coordinate', parser) limit2_parsed = parse_as_number_or_datetime(limit2, 'subset range start coordinate', parser) limit_dict[dim_name] = SubsetLimits(limit1_parsed, limit2_parsed, is_time) return limit_dict
def parse_datetime_passed_error_message_to_parser(): parser = MockParser() name = 'date/time arg' try: dt = parse_datetime('2X10', name, parser) except MockParserError as e: assert e.args[0].index(name) > 0
def get_aggregate_grid(aggregategrid, parser): """ :param aggregategrid: List of aggregate grid specifications :param parser: The parser used to report errors :return: The parsed datagroups as a list of dictionaries """ from cis.parse_datetime import parse_datetime, parse_datetime_delta, parse_as_number_or_datetime from cis.aggregation.aggregation_grid import AggregationGrid # Split into the limits for each dimension. split_input = split_outside_brackets(aggregategrid) if len(split_input) == 0: parser.error("Limits for at least one dimension must be specified for aggregation") grid_dict = {} for seg in split_input: # Parse out dimension name and new grid spacing; the expected format is: # <dim_name>=[<start_value>,<end_value,<delta>] match = re.match(r'(?P<dim>[^=]+)(?:=)?(?:\[(?P<start>[^],]+)?(?:,(?P<end>[^],]+))?(?:,(?P<delta>[^]]+))?\])?', seg) if match is None or match.group('dim') is None: parser.error("A dimension for aggregation does not have a valid dimension name") elif match.group('start') is None and match.group('delta') is None: dim_name = match.group('dim') grid_dict[dim_name] = AggregationGrid(float('NaN'), float('NaN'), float('NaN'), None) elif match.group('end') is None: parser.error("A dimension for aggregation has a start point but no end or delta value, an end and a delta " "value must be supplied, for example x=[0,360,30].") elif match.group('delta') is None: parser.error("A dimension for aggregation has a start point but no delta value, a delta value must be " "supplied, for example x=[0,360,30].") else: dim_name = match.group('dim') start = match.group('start') end = match.group('end') delta = match.group('delta') # If the dimension is specified as x, y, z, p or t, assume that the dimension is spatial or temporal in the # obvious way. Otherwise, parse what is found as a date/time or number. is_time = None if dim_name.lower() == 't': start_parsed = parse_datetime(start, 'aggregation grid start date/time', parser) end_parsed = parse_datetime(end, 'aggregation grid end date/time', parser) delta_parsed = parse_datetime_delta(delta, 'aggregation grid delta date/time', parser) is_time = True elif dim_name.lower() in ['x', 'y', 'z', 'p']: start_parsed = parse_float(start, 'aggregation grid start coordinate', parser) end_parsed = parse_float(end, 'aggregation grid end coordinate', parser) delta_parsed = parse_float(delta, 'aggregation grid delta coordinate', parser) is_time = False if dim_name.lower() == 'x': if not start_parsed <= end_parsed: parser.error("Longitude grid must be monotonically increasing (i.e. for x[A,B,C] A <= B). For " "example, x=[90,-90,10] is invalid but x=[90,270,10] is valid") if not end_parsed - start_parsed <= 360: parser.error("Longitude grid should not be wider than 360 degrees " "(i.e. for x[A,B,C] B-A <= 360)") else: start_parsed = parse_as_number_or_datetime(start, 'aggregation grid start coordinate', parser) end_parsed = parse_as_number_or_datetime(end, 'aggregation grid end coordinate', parser) delta_parsed = parse_as_number_or_datetime(delta, 'aggregation grid delta coordinate', parser) grid_dict[dim_name] = AggregationGrid(start_parsed, end_parsed, delta_parsed, is_time) return grid_dict
def parse_datetime_raises_error_if_invalid_character_in_year(): parser = MockParser() dt = parse_datetime('2X10', 'date/time arg', parser)
def parse_datetime_can_parse_date_hour_min_sec_no_leading_zeros(): parser = MockParser() dt = parse_datetime('2010-3-4T5:6:7', 'date/time arg', parser) assert (dt == [2010, 3, 4, 5, 6, 7])
def parse_datetime_can_parse_date_time_with_colon_separator(): parser = MockParser() dt = parse_datetime('2010-07-01:13:27:43', 'date/time arg', parser) assert (dt == [2010, 7, 1, 13, 27, 43])
def parse_datetime_can_parse_date_hour(): parser = MockParser() dt = parse_datetime('2010-07-01T13', 'date/time arg', parser) assert (dt == [2010, 7, 1, 13])
def parse_datetime_can_parse_date_hour_min_sec(): parser = MockParser() dt = parse_datetime('2010-07-01T13:27:43', 'date/time arg', parser) assert (dt == [2010, 7, 1, 13, 27, 43])
def parse_datetime_can_parse_year_month(): parser = MockParser() dt = parse_datetime('2010-07', 'date/time arg', parser) print(dt) assert (dt == [2010, 7])
def parse_datetime_can_parse_date(): parser = MockParser() dt = parse_datetime('2010-07-01', 'date/time arg', parser) assert (dt == [2010, 7, 1])
def parse_datetime_can_parse_year(): parser = MockParser() dt = parse_datetime('2010', 'date/time arg', parser) assert (dt == [2010])
def parse_datetime_raises_error_if_invalid_day(): parser = MockParser() dt = parse_datetime('2010-06-31', 'date/time arg', parser)
def parse_datetime_raises_error_if_invalid_month(): parser = MockParser() dt = parse_datetime('2010-13', 'date/time arg', parser)
def parse_datetime_raises_error_if_too_many_time_components(): parser = MockParser() dt = parse_datetime('2010-10-05T12:01:02:03', 'date/time arg', parser)
def parse_datetime_raises_error_if_time_but_incomplete_date(): parser = MockParser() dt = parse_datetime('2010-10T12:00', 'date/time arg', parser)