def step_either_or_expected_not_both(context, xpath_expression1, xpath_expression2): errors = [] fail_msg_neither = 'Neither {} nor {} have been found' fail_msg_both = 'Either {} or {} are expected (not both)' xpaths1 = get_xobjects(context.xml, xpath_expression1) xpaths2 = get_xobjects(context.xml, xpath_expression2) if not xpaths1 and not xpaths2: errors = [{'explanation': fail_msg_neither.format(xpath_expression1, xpath_expression2), 'path': get_child_full_xpath(context.xml, context.xml)}] if xpaths1 and xpaths2: msg = fail_msg_both.format(xpath_expression1, xpath_expression2) if len(xpaths1) == len(xpaths2): zipped_xpaths = zip(xpaths1, xpaths2) for tup_xpath1, tup_xpath2 in zipped_xpaths: errors = [{ 'explanation': msg, 'path': '{} & {}'.format(get_child_full_xpath(context.xml, tup_xpath1), get_child_full_xpath(context.xml, tup_xpath2)) }] else: for xpath in xpaths2: errors.append({ 'explanation': msg, 'path': '{} & {}'.format(get_child_full_xpath(context.xml, xpaths1[0]), get_child_full_xpath(context.xml, xpath)) }) return context, errors
def step_start_date_before_end_date(context, xpath1, xpath2): parents = get_xobjects(context.xml, context.xpath_expression) errors = [] for parent in parents: start_date_attrs = get_xobjects(parent, xpath1) end_date_attrs = get_xobjects(parent, xpath2) if not start_date_attrs or not end_date_attrs: continue start_date_attr = start_date_attrs[0] end_date_attr = end_date_attrs[0] if not invalid_date_format( start_date_attr) and not invalid_date_format(end_date_attr): start_date = datetime.datetime.strptime(start_date_attr, '%Y-%m-%d').date() end_date = datetime.datetime.strptime(end_date_attr, '%Y-%m-%d').date() if start_date >= end_date: fail_msg = 'Start date ({}) must be before end date ({})' path_attr1 = get_child_full_xpath(context.xml, start_date_attr) path_attr2 = get_child_full_xpath(context.xml, end_date_attr) errors.append({ 'explanation': fail_msg.format(start_date_attr, end_date_attr), 'path': '{} & {}'.format(path_attr1, path_attr2) }) return context, errors
def step_start_date_before_end_date(context, xpath1, xpath2): parents = get_xobjects(context.xml, context.xpath_expression) errors = [] for parent in parents: start_date_attrs = get_xobjects(parent, xpath1) end_date_attrs = get_xobjects(parent, xpath2) if not start_date_attrs or not end_date_attrs: continue start_date_attr = start_date_attrs[0] end_date_attr = end_date_attrs[0] if not invalid_date_format(start_date_attr) and not invalid_date_format(end_date_attr): start_date = datetime.datetime.strptime(start_date_attr, '%Y-%m-%d').date() end_date = datetime.datetime.strptime(end_date_attr, '%Y-%m-%d').date() if start_date >= end_date: fail_msg = 'Start date ({}) must be before end date ({})' path_attr1 = get_child_full_xpath(context.xml, start_date_attr) path_attr2 = get_child_full_xpath(context.xml, end_date_attr) errors.append({ 'explanation': fail_msg.format(start_date_attr, end_date_attr), 'path': '{} & {}'.format(path_attr1, path_attr2) }) return context, errors
def step_must_be_today_or_past(context, xpath): errors = [] today = datetime.date.today() parents = get_xobjects(context.xml, context.xpath_expression) for parent in parents: date_attrs = get_xobjects(parent, xpath) if not date_attrs: continue date_attr = date_attrs[0] if invalid_date_format(date_attr): continue date = datetime.datetime.strptime(date_attr, '%Y-%m-%d').date() if date > today: errors.append({ 'explanation': '{} must be on or before today ({})'.format(date, today), 'path': get_child_full_xpath(context.xml, date_attr) }) return context, errors
def step_openag_element_has_child_element(context, xpath_expression2): errors = [] fail_msg = '{} must contain a {} element' for xpath in get_xobjects(context.xml, context.xpath_expression): has_xpath_expression2 = get_xobjects(xpath, xpath_expression2) if not has_xpath_expression2: errors.append({ 'explanation': fail_msg.format(context.xpath_expression, xpath_expression2), 'path': get_child_full_xpath(context.xml, xpath) }) return context, errors
def step_either_or_expected(context, xpath_expression1, xpath_expression2): errors = [] fail_msg_neither = 'Neither {} nor {} have been found' parents = get_xobjects(context.xml, context.xpath_expression) for parent in parents: xpaths1 = get_xobjects(parent, xpath_expression1) xpaths2 = get_xobjects(parent, xpath_expression2) if not xpaths1 and not xpaths2: errors.append({'explanation': fail_msg_neither.format(xpath_expression1, xpath_expression2), 'path': get_child_full_xpath(context.xml, context.xml)}) return context, errors
def step_attr_add_to_hundred(context, attribute): elements = get_xobjects(context.xml, context.xpath_expression) errors = [] if len(elements) == 0 or (len(elements) == 1 and elements[0].attrib.get( attribute, '100') == '100'): return context, errors attr_sum = 0 for el in elements: percentage = el.attrib.get(attribute) if percentage: try: attr_sum += Decimal(percentage) except InvalidOperation: pass if attr_sum != 100: errors.append({ 'explanation': '{}/@{} adds up to {}%'.format(context.xpath_expression, attribute, attr_sum), 'path': ' & '.join( get_child_full_xpath(context.xml, element) for element in elements) }) return context, errors
def step_not_expected(context, xpath_expression): errors = [] for xpath in get_xobjects(context.xml, xpath_expression): errors.append({'explanation': '`{}` is not expected'.format(xpath_expression), 'path': get_child_full_xpath(context.xml, xpath)}) return context, errors
def step_either_or_expected(context, xpath_expression1, xpath_expression2): errors = [] fail_msg_neither = 'Neither {} nor {} have been found' parents = get_xobjects(context.xml, context.xpath_expression) for parent in parents: xpaths1 = get_xobjects(parent, xpath_expression1) xpaths2 = get_xobjects(parent, xpath_expression2) if not xpaths1 and not xpaths2: errors.append({ 'explanation': fail_msg_neither.format(xpath_expression1, xpath_expression2), 'path': get_child_full_xpath(context.xml, context.xml) }) return context, errors
def step_openag_expected(context, xpath_expression): errors = [] if not get_xobjects(context.xml, xpath_expression): errors = [{ 'explanation': 'the activity should include at least one {} element'.format( xpath_expression), 'path': get_child_full_xpath(context.xml, context.xml) }] return context, errors
def step_not_expected(context, xpath_expression): errors = [] for xpath in get_xobjects(context.xml, xpath_expression): errors.append({ 'explanation': '`{}` is not expected'.format(xpath_expression), 'path': get_child_full_xpath(context.xml, xpath) }) return context, errors
def step_must_be_today_or_past(context, xpath): errors = [] today = datetime.date.today() parents = get_xobjects(context.xml, context.xpath_expression) for parent in parents: date_attrs = get_xobjects(parent, xpath) if not date_attrs: continue date_attr = date_attrs[0] if invalid_date_format(date_attr): continue date = datetime.datetime.strptime(date_attr, '%Y-%m-%d').date() if date > today: errors.append({'explanation': '{} must be on or before today ({})'.format(date, today), 'path': get_child_full_xpath(context.xml, date_attr)}) return context, errors
def step_either_or_expected_not_both(context, xpath_expression1, xpath_expression2): errors = [] fail_msg_neither = 'Neither {} nor {} have been found' fail_msg_both = 'Either {} or {} are expected (not both)' xpaths1 = get_xobjects(context.xml, xpath_expression1) xpaths2 = get_xobjects(context.xml, xpath_expression2) if not xpaths1 and not xpaths2: errors = [{ 'explanation': fail_msg_neither.format(xpath_expression1, xpath_expression2), 'path': get_child_full_xpath(context.xml, context.xml) }] if xpaths1 and xpaths2: msg = fail_msg_both.format(xpath_expression1, xpath_expression2) if len(xpaths1) == len(xpaths2): zipped_xpaths = zip(xpaths1, xpaths2) for tup_xpath1, tup_xpath2 in zipped_xpaths: errors = [{ 'explanation': msg, 'path': '{} & {}'.format( get_child_full_xpath(context.xml, tup_xpath1), get_child_full_xpath(context.xml, tup_xpath2)) }] else: for xpath in xpaths2: errors.append({ 'explanation': msg, 'path': '{} & {}'.format( get_child_full_xpath(context.xml, xpaths1[0]), get_child_full_xpath(context.xml, xpath)) }) return context, errors
def step_openag_org_id_prefix_expected(context, attribute): errors = [] fail_msg = '@{} {} does not start with a recognised org-ids prefix' for xpath in get_xobjects(context.xml, context.xpath_expression): attr_id = xpath.attrib.get(attribute, '') for prefix in ORGIDS_PREFIXES: if attr_id.startswith(prefix): break else: errors.append({'explanation': fail_msg.format(attribute, attr_id), 'path': '{}/@{}'.format(get_child_full_xpath(context.xml, xpath), attribute)}) return context, errors
def step_iati_id_text_match_regex(context, regex_str): regex = re.compile(regex_str) errors = [] xpath = get_xobjects(context.xml, context.xpath_expression) if xpath: xpath = xpath[0] fail_msg = 'Text does not match the regex {}' text_str = xpath.text if not regex.match(text_str): errors = [{'explanation': fail_msg.format(text_str, regex_str), 'path': '{}/text()'.format(get_child_full_xpath(context.xml, xpath))}] return context, errors
def step_attribute_match_regex(context, attribute, regex_str): regex = re.compile(regex_str) errors = [] fail_msg = '{} does not match the regex {}' for xpath in get_xobjects(context.xml, context.xpath_expression): if attribute not in xpath.attrib: continue attr_str = xpath.attrib[attribute] if not regex.match(attr_str): errors.append({'explanation': fail_msg.format(attr_str, regex_str), 'path': '{}/@{}'.format(get_child_full_xpath(context.xml, xpath), attribute)}) return context, errors
def step_openag_tag_attribute_expected(context, attribute): errors = [] fail_msg = '{} element must have @{} attribute' for xpath in get_xobjects(context.xml, context.xpath_expression): attrib = xpath.attrib required_attrib = attrib.get(attribute) if not required_attrib: errors.append({ 'explanation': fail_msg.format(context.xpath_expression, attribute), 'path': get_child_full_xpath(context.xml, xpath) }) return context, errors
def step_iati_id_text_match_regex(context, regex_str): regex = re.compile(regex_str) errors = [] xpath = get_xobjects(context.xml, context.xpath_expression) if xpath: xpath = xpath[0] fail_msg = 'Text does not match the regex {}' text_str = xpath.text if not regex.match(text_str): errors = [{ 'explanation': fail_msg.format(text_str, regex_str), 'path': '{}/text()'.format(get_child_full_xpath(context.xml, xpath)) }] return context, errors
def step_openag_org_id_prefix_expected(context, attribute): errors = [] fail_msg = '@{} {} does not start with a recognised org-ids prefix' for xpath in get_xobjects(context.xml, context.xpath_expression): attr_id = xpath.attrib.get(attribute, '') for prefix in ORGIDS_PREFIXES: if attr_id.startswith(prefix): break else: errors.append({ 'explanation': fail_msg.format(attribute, attr_id), 'path': '{}/@{}'.format(get_child_full_xpath(context.xml, xpath), attribute) }) return context, errors
def step_attribute_match_regex(context, attribute, regex_str): regex = re.compile(regex_str) errors = [] fail_msg = '{} does not match the regex {}' for xpath in get_xobjects(context.xml, context.xpath_expression): if attribute not in xpath.attrib: continue attr_str = xpath.attrib[attribute] if not regex.match(attr_str): errors.append({ 'explanation': fail_msg.format(attr_str, regex_str), 'path': '{}/@{}'.format(get_child_full_xpath(context.xml, xpath), attribute) }) return context, errors
def step_openag_tag_attribute_accepted_values(context, attribute, any_value): errors = [] fail_msg = '"{}" is not a valid value for @{} attribute (it should be "{}")' for xpath in get_xobjects(context.xml, context.xpath_expression): element_attrs = xpath.attrib matching_value = any([ element_attrs.get(attribute) == val for val in any_value.split(' or ') ]) if not matching_value: errors.append({ 'explanation': fail_msg.format(element_attrs.get(attribute), attribute, any_value), 'path': '{}/@{}'.format(get_child_full_xpath(context.xml, xpath), attribute) }) return context, errors
def step_attr_add_to_hundred(context, attribute): elements = get_xobjects(context.xml, context.xpath_expression) errors = [] if len(elements) == 0 or (len(elements) == 1 and elements[0].attrib.get(attribute, '100') == '100'): return context, errors attr_sum = 0 for el in elements: percentage = el.attrib.get(attribute) if percentage: try: attr_sum += Decimal(percentage) except InvalidOperation: pass if attr_sum != 100: errors.append({'explanation': '{}/@{} adds up to {}%'.format(context.xpath_expression, attribute, attr_sum), 'path': ' & '.join(get_child_full_xpath(context.xml, element) for element in elements)}) return context, errors