Source code for pyx12.map_if._loop

######################################################################
# Copyright (c)
# All rights reserved.
#
# This software is licensed as described in the file LICENSE.txt, which
# you should have received as part of this distribution.
#
######################################################################
"""
Loop interface ------ recursive container of loops and segments.
"""

from __future__ import annotations

import sys
from collections.abc import Iterator
from typing import TYPE_CHECKING
from xml.etree.ElementTree import Element

import pyx12.segment

from ..errors import EngineError
from ..path import X12Path
from ._base import MAXINT, _required_attr, x12_node
from ._segment import segment_if

if TYPE_CHECKING:
    from ._root import map_if


############################################################
# Loop Interface
############################################################
[docs] class loop_if(x12_node): """ Loop Interface """ root: map_if pos_map: dict[int, list[x12_node]] base_name: str _cur_count: int type: str | None pos: int repeat: str | None def __init__(self, root: map_if, parent: x12_node, elem: Element) -> None: """ """ x12_node.__init__(self) self.root = root self.parent = parent self.pos_map = {} self.base_name = "loop" self._cur_count = 0 self.id = elem.get("xid") self.path = self.id or "" self.type = elem.get("type") self.name = elem.get("name") if elem.get("name") else elem.findtext("name") self.usage = elem.get("usage") if elem.get("usage") else elem.findtext("usage") self.pos = int(_required_attr(elem, "pos")) self.repeat = elem.get("repeat") if elem.get("repeat") else elem.findtext("repeat") for e in elem.findall("loop"): loop_node = loop_if(self.root, self, e) try: self.pos_map[loop_node.pos].append(loop_node) except KeyError: self.pos_map[loop_node.pos] = [loop_node] for e in elem.findall("segment"): seg_node = segment_if(self.root, self, e) try: self.pos_map[seg_node.pos].append(seg_node) except KeyError: self.pos_map[seg_node.pos] = [seg_node] # For the segments with duplicate ordinals, adjust the path to be unique for ord1 in sorted(self.pos_map): if len(self.pos_map[ord1]) > 1: for seg_node in [n for n in self.pos_map[ord1] if isinstance(n, segment_if)]: id_elem = seg_node.guess_unique_key_id_element() if id_elem is not None: seg_node.path = seg_node.path + "[" + (id_elem.valid_codes[0] or "") + "]"
[docs] def debug_print(self) -> None: sys.stdout.write(self.__repr__()) for ord1 in sorted(self.pos_map): for node in self.pos_map[ord1]: if isinstance(node, (loop_if, segment_if)): node.debug_print()
def __len__(self) -> int: i = 0 for ord1 in sorted(self.pos_map): i += len(self.pos_map[ord1]) return i def __repr__(self) -> str: """ :rtype: string """ out = "" if self.id: out += "LOOP %s" % (self.id) if self.name: out += ' "%s"' % (self.name) if self.usage: out += " usage: %s" % (self.usage) if self.pos: out += " pos: %s" % (self.pos) if self.repeat: out += " repeat: %s" % (self.repeat) out += "\n" return out
[docs] def get_max_repeat(self) -> int: if self.repeat is None: return MAXINT if self.repeat == ">1" or self.repeat == ">1": return MAXINT return int(self.repeat)
[docs] def get_parent(self) -> x12_node | None: return self.parent
[docs] def get_first_node(self) -> x12_node | None: pos_keys = sorted(self.pos_map) if len(pos_keys) > 0: return self.pos_map[pos_keys[0]][0] else: return None
[docs] def get_first_seg(self) -> segment_if | None: first = self.get_first_node() if isinstance(first, segment_if): return first else: return None
[docs] def childIterator(self) -> Iterator[x12_node]: for ord1 in sorted(self.pos_map): yield from self.pos_map[ord1]
[docs] def getnodebypath(self, spath: str) -> x12_node | None: """ :param spath: remaining path to match :type spath: string :return: matching node, or None is no match """ pathl = spath.split("/") if len(pathl) == 0: return None for ord1 in sorted(self.pos_map): for child in self.pos_map[ord1]: assert child.id is not None if isinstance(child, loop_if): if child.id.upper() == pathl[0].upper(): if len(pathl) == 1: return child else: return child.getnodebypath("/".join(pathl[1:])) elif isinstance(child, segment_if) and len(pathl) == 1: if pathl[0].find("[") == -1: # No id to match if pathl[0] == child.id: return child else: seg_id = pathl[0][0 : pathl[0].find("[")] id_val = pathl[0][pathl[0].find("[") + 1 : pathl[0].find("]")] if seg_id == child.id: possible = child.get_unique_key_id_element(id_val) if possible is not None: return child raise EngineError('getnodebypath failed. Path "%s" not found' % spath)
[docs] def getnodebypath2(self, path_str: str) -> x12_node | None: """ Try x12 path :param path_str: remaining path to match :type path_str: string :return: matching node, or None is no match """ x12path = X12Path(path_str) if x12path.empty(): return None for ord1 in sorted(self.pos_map): for child in self.pos_map[ord1]: assert child.id is not None if isinstance(child, loop_if) and len(x12path.loop_list) > 0: if child.id.upper() == x12path.loop_list[0].upper(): if len(x12path.loop_list) == 1 and x12path.seg_id is None: return child else: del x12path.loop_list[0] return child.getnodebypath2(x12path.format()) elif ( isinstance(child, segment_if) and len(x12path.loop_list) == 0 and x12path.seg_id is not None ): if x12path.id_val is None: if x12path.seg_id == child.id: return child.getnodebypath2(x12path.format()) else: seg_id = x12path.seg_id id_val = x12path.id_val if seg_id == child.id: possible = child.get_unique_key_id_element(id_val) if possible is not None: return child.getnodebypath2(x12path.format()) raise EngineError('getnodebypath2 failed. Path "%s" not found' % path_str)
[docs] def get_child_count(self) -> int: return self.__len__()
[docs] def get_child_node_by_idx(self, idx: int) -> x12_node | None: """ :param idx: zero based """ raise EngineError("loop_if.get_child_node_by_idx is not a valid call for a loop_if")
[docs] def get_seg_count(self) -> int: """ :return: Number of child segments :rtype: integer """ i = 0 for ord1 in sorted(self.pos_map): for child in self.pos_map[ord1]: if child.is_segment(): i += 1 return i
[docs] def is_loop(self) -> bool: """ :rtype: boolean """ return True
[docs] def is_match(self, seg_data: pyx12.segment.Segment) -> bool: """ :type seg_data: L{segment<segment.Segment>} :return: Is the segment a match to this loop? :rtype: boolean """ pos_keys = sorted(self.pos_map) child = self.pos_map[pos_keys[0]][0] if isinstance(child, loop_if): return bool(child.is_match(seg_data)) elif isinstance(child, segment_if): if child.is_match(seg_data): return True else: return False # seg does not match the first segment in loop, so not valid else: return False
[docs] def get_child_seg_node(self, seg_data: pyx12.segment.Segment) -> segment_if | None: """ Return the child segment matching the segment data """ for child in self.childIterator(): if isinstance(child, segment_if) and child.is_match(seg_data): return child return None
[docs] def get_child_loop_node(self, seg_data: pyx12.segment.Segment) -> loop_if | None: """ Return the child segment matching the segment data """ for child in self.childIterator(): if isinstance(child, loop_if) and child.is_match(seg_data): return child return None
[docs] def get_cur_count(self) -> int: """ :return: current count :rtype: int """ raise DeprecationWarning("Moved to nodeCounter")
[docs] def incr_cur_count(self) -> None: raise DeprecationWarning("Moved to nodeCounter")
[docs] def reset_child_count(self) -> None: """ Set cur_count of child nodes to zero """ raise DeprecationWarning("Moved to nodeCounter")
[docs] def reset_cur_count(self) -> None: """ Set cur_count of node and child nodes to zero """ raise DeprecationWarning("Moved to nodeCounter")
[docs] def set_cur_count(self, ct: int) -> None: raise DeprecationWarning("Moved to nodeCounter")
[docs] def get_counts_list(self, ct_list: list[tuple[str, int]]) -> bool: """ Build a list of (path, ct) of the current node and parents Gets the node counts to apply to another map :param ct_list: List to append to :type ct_list: list[(string, int)] """ raise DeprecationWarning("Moved to nodeCounter")
[docs] def loop_segment_iterator(self) -> Iterator[x12_node]: yield self for ord1 in sorted(self.pos_map): for child in self.pos_map[ord1]: if isinstance(child, loop_if): yield from child.loop_segment_iterator() elif isinstance(child, segment_if): yield child