Exemple #1
0
def parse_extinstance(xml_element, context):
    # get extinstance reference id
    ref = InstanceId(xml_element.text, None)

    # pull referred instance element
    referred_elements = xml_element.xpath(
        f"//{INSTANCE}[{ID_ATTR}='{ref.id}']")
    if not referred_elements:
        # TODO make a single call?
        msg = f"Dangling reference {ref}"
        warnings.warn(msg, SyntaxWarning)
        LOG.warning(msg)
        return None
    referred_element = referred_elements[0]

    # process extinstance template
    #  - we want the individual instances in this case.
    #    may be getting appended to local instances and needs to be a list of instances
    instance = read_instance(referred_element, context)
    instances = instance.unroll()

    # FOREIGNKEY = constrains returned instance set
    has_foreign_key = len(
        referred_element.xpath(f'./child::{CONTAINER}/{FOREIGNKEY}')) > 0

    if not has_foreign_key:
        # No screening criteria, return List of resolved instances
        result = instances
    else:
        element = get_children(referred_element, CONTAINER)[0]
        result = group_extinstances(instances, element, context)

    return result
Exemple #2
0
def parse_id(context, xml_element, instance_class):
    keys = None
    primary_key_elements = xml_element.xpath(f"./{PRIMARYKEY}")
    if primary_key_elements:
        keys = parse_identifier_field(context, primary_key_elements[0])
    ids = xml_element.xpath(ID_ATTR)
    if not ids:
        # Randomly generate an ID for each instance that doesn't have any.
        id = f"{instance_class.vodml_id}-{str(uuid.uuid4())}"
    else:
        id = ids[0]

    return InstanceId(id, keys)
Exemple #3
0
def parse_idref(xml_element, context):
    ref = InstanceId(xml_element.text, None)

    referred_elements = xml_element.xpath(
        f"//{INSTANCE}[{ID_ATTR}='{ref.id}']")

    if not referred_elements:
        # TODO make a single call?
        msg = f"Dangling reference {ref}"
        warnings.warn(msg, SyntaxWarning)
        LOG.warning(msg)
        return None

    referred_element = referred_elements[0]
    return SingleReferenceWrapper(read_instance(referred_element, context))
Exemple #4
0
def parse_foreign_key(xml_element, context):
    ref = InstanceId(None, parse_identifier_field(context, xml_element))

    target_id = xml_element.xpath(f"./{TARGETID}")[0].text
    referred_elements = xml_element.xpath(
        f"//*[{ID_ATTR}='{target_id}']/{INSTANCE}")

    instances = [
        read_instance(referred_element, context)
        for referred_element in referred_elements
    ]
    instances_index = {
        tuple(instance.__vo_id__.keys): instance
        for instance in instances
    }
    references = [instances_index.get(tuple(key), None) for key in ref.keys]
    return RowReferenceWrapper(references)
Exemple #5
0
def group_extinstances(instances, xml_element, context):
    # ----------------------------------------------------------------------
    # instances: List of resolved EXTINSTANCEs
    # xml_element: CONTAINER element
    #----------------------------------------------------------------------
    # CONTAINER is a backward connection to a parent instance.
    #   - Contains one of: IDREF, FOREIGNKEY, REMOTEREF
    #      - implementing FOREIGNKEY

    fk_elements = get_children(xml_element, FOREIGNKEY)
    for fk_element in fk_elements:
        target_id = fk_element.xpath(f"./{TARGETID}")[0].text

        # Get keys associated with each instance
        instance_keys = parse_identifier_field(context, fk_element)
        #print("DEBUG: EXTINSTANCE KEYS = %s"%(str(instance_keys)))

        # Get target keys
        target = context.get_instance_by_id(InstanceId(target_id, None))
        if target is not None:
            # Untested
            target_instance_keys = target.__vo_id__.keys
        else:
            # Target instance not already processed..
            # NOTE: do not want to 'make' it, can set up recursive loop
            #  ie: we are currently processing the Target instance.
            target_elements = xml_element.xpath(
                f"//*[{ID_ATTR}='{target_id}']")
            target_element = target_elements[0]

            # Resolve PRIMARYKEY value set from target element:
            #   pulled from read_instance() and make()
            type_id = resolve_type(target_element)
            element_class = context.get_type_by_id(type_id)
            target_instance_id = parse_id(context, target_element,
                                          element_class)
            target_instance_keys = target_instance_id.keys
            #print("DEBUG: TARGET instance keys = %s"%(str(target_instance_keys)))

    # Have keys resolved.. sort instances
    # target instance keys are the selection criteria
    sorted_instances = {}
    for key in target_instance_keys:
        matches = [
            instances[n] for n in range(len(instances))
            if (tuple(key) == tuple(instance_keys[n]))
        ]
        sorted_instances[tuple(key)] = matches

    # We want to return slices of the sorted_instances, with 1 instance per target 'row'
    #   - determine maximumn # matches; this is # slices to return`
    #   - pad each entry to the same length (with None)
    #   - generate slices
    result = []
    max_matches = max([len(matches) for matches in sorted_instances.values()])

    for values in sorted_instances.values():
        values += [None] * (max_matches - len(values))

    for n in range(max_matches):
        group = [
            sorted_instances[tuple(key)][n] for key in target_instance_keys
        ]
        result.append(group)

    return result