pyx12 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyx12/__init__.py +10 -0
- pyx12/codes.py +120 -0
- pyx12/dataele.py +103 -0
- pyx12/decorators.py +79 -0
- pyx12/errh_json.py +161 -0
- pyx12/errh_xml.py +324 -0
- pyx12/error_997.py +457 -0
- pyx12/error_999.py +391 -0
- pyx12/error_debug.py +148 -0
- pyx12/error_handler.py +1359 -0
- pyx12/error_html.py +255 -0
- pyx12/error_item.py +102 -0
- pyx12/error_visitor.py +80 -0
- pyx12/errors.py +73 -0
- pyx12/map/270.4010.X092.A1.xml +2995 -0
- pyx12/map/271.4010.X092.A1.xml +4874 -0
- pyx12/map/276.4010.X093.A1.xml +2166 -0
- pyx12/map/277.4010.X093.A1.xml +2669 -0
- pyx12/map/277.5010.X212.xml +4179 -0
- pyx12/map/277.5010.X214.xml +2605 -0
- pyx12/map/277U.4010.X070.xml +2668 -0
- pyx12/map/278.4010.X094.27.A1.xml +7321 -0
- pyx12/map/278.4010.X094.A1.xml +9385 -0
- pyx12/map/820.4010.X061.A1.xml +2313 -0
- pyx12/map/820.5010.X218.v2.xml +1029 -0
- pyx12/map/820.5010.X218.xml +3142 -0
- pyx12/map/830.4010.PS.xml +881 -0
- pyx12/map/834.4010.X095.A1.xml +4032 -0
- pyx12/map/834.5010.X220.A1.v2.xml +2145 -0
- pyx12/map/834.5010.X220.A1.xml +5127 -0
- pyx12/map/835.4010.X091.A1.xml +3587 -0
- pyx12/map/835.5010.X221.A1.v2.xml +1410 -0
- pyx12/map/835.5010.X221.A1.xml +4233 -0
- pyx12/map/837.4010.X096.A1.xml +39589 -0
- pyx12/map/837.4010.X097.A1.xml +11478 -0
- pyx12/map/837.4010.X098.A1.xml +23842 -0
- pyx12/map/837.5010.X222.A1.xml +25900 -0
- pyx12/map/837Q3.I.5010.X223.A1.v2.xml +9987 -0
- pyx12/map/837Q3.I.5010.X223.A1.xml +30108 -0
- pyx12/map/841.4010.XXXC.xml +865 -0
- pyx12/map/997.4010.xml +961 -0
- pyx12/map/999.5010.xml +1283 -0
- pyx12/map/999.5010X231.A1.xml +1283 -0
- pyx12/map/README +16 -0
- pyx12/map/README.md +20 -0
- pyx12/map/codes.xml +2844 -0
- pyx12/map/codes.xsd +40 -0
- pyx12/map/comp_test.xml +69 -0
- pyx12/map/dataele.xml +363 -0
- pyx12/map/dataele.xsd +60 -0
- pyx12/map/dump.py +22 -0
- pyx12/map/map.v2.xsd +135 -0
- pyx12/map/map.xsd +150 -0
- pyx12/map/maps.xml +85 -0
- pyx12/map/maps.xsd +109 -0
- pyx12/map/ord.py +39 -0
- pyx12/map/x12.control.00401.xml +386 -0
- pyx12/map/x12.control.00501.xml +369 -0
- pyx12/map/x12simple.dtd +16 -0
- pyx12/map_if/__init__.py +39 -0
- pyx12/map_if/_base.py +189 -0
- pyx12/map_if/_composite.py +160 -0
- pyx12/map_if/_element.py +391 -0
- pyx12/map_if/_loader.py +58 -0
- pyx12/map_if/_loop.py +318 -0
- pyx12/map_if/_root.py +222 -0
- pyx12/map_if/_segment.py +513 -0
- pyx12/map_index.py +146 -0
- pyx12/map_override.py +45 -0
- pyx12/map_walker.py +695 -0
- pyx12/nodeCounter.py +88 -0
- pyx12/params.py +160 -0
- pyx12/path.py +218 -0
- pyx12/rawx12file.py +98 -0
- pyx12/scripts/__init__.py +22 -0
- pyx12/scripts/x12html.py +166 -0
- pyx12/scripts/x12info.py +107 -0
- pyx12/scripts/x12norm.py +106 -0
- pyx12/scripts/x12valid.py +197 -0
- pyx12/scripts/x12xml.py +153 -0
- pyx12/scripts/xmlx12.py +105 -0
- pyx12/segment.py +589 -0
- pyx12/syntax.py +147 -0
- pyx12/validation.py +275 -0
- pyx12/version.py +1 -0
- pyx12/x12context.py +1250 -0
- pyx12/x12file.py +687 -0
- pyx12/x12metadata.py +278 -0
- pyx12/x12n_document.py +290 -0
- pyx12/x12xml.py +234 -0
- pyx12/x12xml_simple.py +128 -0
- pyx12/xmlwriter.py +155 -0
- pyx12/xmlx12_simple.py +60 -0
- pyx12-4.0.0.dist-info/METADATA +173 -0
- pyx12-4.0.0.dist-info/RECORD +99 -0
- pyx12-4.0.0.dist-info/WHEEL +5 -0
- pyx12-4.0.0.dist-info/entry_points.txt +7 -0
- pyx12-4.0.0.dist-info/licenses/LICENSE.txt +28 -0
- pyx12-4.0.0.dist-info/top_level.txt +1 -0
pyx12/__init__.py
ADDED
pyx12/codes.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# Copyright
|
|
3
|
+
# John Holland <john@zoner.org>
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This software is licensed as described in the file LICENSE.txt, which
|
|
7
|
+
# you should have received as part of this distribution.
|
|
8
|
+
#
|
|
9
|
+
######################################################################
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
External Codes interface
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os.path
|
|
19
|
+
from importlib.resources import files as _res_files
|
|
20
|
+
from typing import IO, Any, TypedDict
|
|
21
|
+
|
|
22
|
+
import defusedxml.ElementTree as et
|
|
23
|
+
|
|
24
|
+
from pyx12.errors import EngineError
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CodesError(Exception):
|
|
28
|
+
"""Class for code modules errors."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _CodeSet(TypedDict):
|
|
32
|
+
name: str | None
|
|
33
|
+
dataele: str | None
|
|
34
|
+
codes: frozenset[str | None]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ExternalCodes:
|
|
38
|
+
"""
|
|
39
|
+
Validates an ID against an external list of codes
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
codes: dict[str | None, _CodeSet]
|
|
43
|
+
exclude_list: list[str]
|
|
44
|
+
|
|
45
|
+
def __init__(self, base_path: str | None = None, exclude: str | None = None) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Initialize the external list of codes
|
|
48
|
+
|
|
49
|
+
:param base_path: Override directory containing codes.xml.
|
|
50
|
+
If None, uses package resource folder.
|
|
51
|
+
:param exclude: comma-separated string of external code names to ignore.
|
|
52
|
+
|
|
53
|
+
``self.codes`` maps codeset_id to ``{'name', 'dataele', 'codes'}``.
|
|
54
|
+
"""
|
|
55
|
+
logger = logging.getLogger("pyx12")
|
|
56
|
+
self.codes = {}
|
|
57
|
+
codes_file = "codes.xml"
|
|
58
|
+
# ElementTree.parse accepts either text- or binary-mode streams; the
|
|
59
|
+
# file source differs between override path and bundled package resource.
|
|
60
|
+
code_fd: IO[Any]
|
|
61
|
+
if base_path is not None:
|
|
62
|
+
logger.debug(f"Looking for codes file '{codes_file}' in map_path '{base_path}'")
|
|
63
|
+
code_fd = open(os.path.join(base_path, codes_file), encoding="utf-8")
|
|
64
|
+
else:
|
|
65
|
+
logger.debug(f"Looking for codes file '{codes_file}' in package resources")
|
|
66
|
+
code_fd = _res_files("pyx12").joinpath("map", codes_file).open("rb")
|
|
67
|
+
|
|
68
|
+
self.exclude_list = exclude.split(",") if exclude is not None else []
|
|
69
|
+
with code_fd:
|
|
70
|
+
parser = et.XMLParser(encoding="utf-8")
|
|
71
|
+
for cElem in et.parse(code_fd, parser=parser).iter("codeset"):
|
|
72
|
+
codeset_id = cElem.findtext("id")
|
|
73
|
+
self.codes[codeset_id] = {
|
|
74
|
+
"name": cElem.findtext("name"),
|
|
75
|
+
"dataele": cElem.findtext("data_ele"),
|
|
76
|
+
"codes": frozenset(c.text for c in cElem.iterfind("version/code")),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def isValid(self, key: str | None, code: str | None, check_dte: str | None = None) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Return True if code is in the codeset identified by key.
|
|
82
|
+
"""
|
|
83
|
+
if not key:
|
|
84
|
+
raise EngineError(f"bad key {key!r}")
|
|
85
|
+
if key in self.exclude_list:
|
|
86
|
+
return True
|
|
87
|
+
if key not in self.codes:
|
|
88
|
+
raise EngineError(f'External Code "{key}" is not defined')
|
|
89
|
+
return code in self.codes[key]["codes"]
|
|
90
|
+
|
|
91
|
+
def debug_print(self, count: int = 10) -> None:
|
|
92
|
+
"""Debug print first <count> codes for each codeset (alphabetical)."""
|
|
93
|
+
for key in self.codes:
|
|
94
|
+
print(sorted(self.codes[key]["codes"], key=lambda c: c or "")[:count])
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def list_external_codesets(base_path: str | None = None) -> list[tuple[str, str]]:
|
|
98
|
+
"""Return ``(id, name)`` for every codeset in ``codes.xml``.
|
|
99
|
+
|
|
100
|
+
Used by CLI scripts to populate ``--help`` with the list of names
|
|
101
|
+
accepted by the ``--exclude-external-codes`` flag. Loads the metadata
|
|
102
|
+
only — does not parse code values — so the cost is small.
|
|
103
|
+
|
|
104
|
+
:param base_path: Override directory containing codes.xml.
|
|
105
|
+
If None, uses package resource folder.
|
|
106
|
+
"""
|
|
107
|
+
code_fd: IO[Any]
|
|
108
|
+
if base_path is not None:
|
|
109
|
+
code_fd = open(os.path.join(base_path, "codes.xml"), encoding="utf-8")
|
|
110
|
+
else:
|
|
111
|
+
code_fd = _res_files("pyx12").joinpath("map", "codes.xml").open("rb")
|
|
112
|
+
pairs: list[tuple[str, str]] = []
|
|
113
|
+
with code_fd:
|
|
114
|
+
parser = et.XMLParser(encoding="utf-8")
|
|
115
|
+
for cs in et.parse(code_fd, parser=parser).iter("codeset"):
|
|
116
|
+
cid = (cs.findtext("id") or "").strip()
|
|
117
|
+
name = (cs.findtext("name") or "").strip()
|
|
118
|
+
if cid:
|
|
119
|
+
pairs.append((cid, name))
|
|
120
|
+
return pairs
|
pyx12/dataele.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# Copyright (c) 2001-2019
|
|
3
|
+
# John Holland <john@zoner.org>
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This software is licensed as described in the file LICENSE.txt, which
|
|
7
|
+
# you should have received as part of this distribution.
|
|
8
|
+
#
|
|
9
|
+
######################################################################
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
Interface to normalized Data Elements
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os.path
|
|
19
|
+
from importlib.resources import files as _res_files
|
|
20
|
+
from typing import IO, Any, TypedDict
|
|
21
|
+
|
|
22
|
+
import defusedxml.ElementTree as et
|
|
23
|
+
|
|
24
|
+
# Intrapackage imports
|
|
25
|
+
from pyx12.errors import EngineError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DataElementsError(Exception):
|
|
29
|
+
"""Class for data elements module errors."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _DataEle(TypedDict):
|
|
33
|
+
data_type: str | None
|
|
34
|
+
min_len: int
|
|
35
|
+
max_len: int
|
|
36
|
+
name: str | None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DataElements:
|
|
40
|
+
"""
|
|
41
|
+
Interface to normalized Data Elements
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
dataele: dict[str | None, _DataEle]
|
|
45
|
+
|
|
46
|
+
def __init__(self, base_path: str | None = None) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Initialize the list of data elements
|
|
49
|
+
|
|
50
|
+
:param base_path: Override directory containing dataele.xml. If None,
|
|
51
|
+
uses package resource folder
|
|
52
|
+
:type base_path: string
|
|
53
|
+
|
|
54
|
+
Note: ``self.dataele`` maps to the data element
|
|
55
|
+
``{ele_num: {data_type, min_len, max_len, name}}``
|
|
56
|
+
"""
|
|
57
|
+
logger = logging.getLogger("pyx12")
|
|
58
|
+
self.dataele = {}
|
|
59
|
+
dataele_file = "dataele.xml"
|
|
60
|
+
# ElementTree.parse accepts either text- or binary-mode streams; the
|
|
61
|
+
# file source differs between override path and bundled package resource.
|
|
62
|
+
fd: IO[Any]
|
|
63
|
+
if base_path is not None:
|
|
64
|
+
logger.debug(
|
|
65
|
+
f"Looking for data element definition file '{dataele_file}' in map_path '{base_path}'"
|
|
66
|
+
)
|
|
67
|
+
fd = open(os.path.join(base_path, dataele_file), encoding="utf-8")
|
|
68
|
+
else:
|
|
69
|
+
logger.debug(
|
|
70
|
+
f"Looking for data element definition file '{dataele_file}' in package resources"
|
|
71
|
+
)
|
|
72
|
+
fd = _res_files("pyx12").joinpath("map", dataele_file).open("rb")
|
|
73
|
+
with fd:
|
|
74
|
+
parser = et.XMLParser(encoding="utf-8")
|
|
75
|
+
for eElem in et.parse(fd, parser=parser).iter("data_ele"):
|
|
76
|
+
ele_num = eElem.get("ele_num")
|
|
77
|
+
self.dataele[ele_num] = {
|
|
78
|
+
"data_type": eElem.get("data_type"),
|
|
79
|
+
"min_len": int(eElem.get("min_len")),
|
|
80
|
+
"max_len": int(eElem.get("max_len")),
|
|
81
|
+
"name": eElem.get("name"),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
def get_by_elem_num(self, ele_num: str | None) -> _DataEle:
|
|
85
|
+
"""
|
|
86
|
+
Get the element characteristics for the indexed element code
|
|
87
|
+
:param ele_num: the data element code
|
|
88
|
+
:type ele_num: string
|
|
89
|
+
:return: {data_type, min_len, max_len, name}
|
|
90
|
+
:rtype: dict
|
|
91
|
+
"""
|
|
92
|
+
if not ele_num:
|
|
93
|
+
raise EngineError(f"Bad data element {ele_num!r}")
|
|
94
|
+
if ele_num not in self.dataele:
|
|
95
|
+
raise EngineError(f'Data Element "{ele_num}" is not defined')
|
|
96
|
+
return self.dataele[ele_num]
|
|
97
|
+
|
|
98
|
+
def debug_print(self) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Debug print data elements
|
|
101
|
+
"""
|
|
102
|
+
for ele_num in self.dataele:
|
|
103
|
+
print(self.dataele[ele_num])
|
pyx12/decorators.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import collections.abc
|
|
4
|
+
import functools
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
# See https://wiki.python.org/moin/PythonDecoratorLibrary
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def dump_args(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
12
|
+
"This decorator dumps out the arguments passed to a function before calling it"
|
|
13
|
+
argnames = func.__code__.co_varnames[: func.__code__.co_argcount]
|
|
14
|
+
fname = func.__name__
|
|
15
|
+
|
|
16
|
+
def echo_func(*args: Any, **kwargs: Any) -> Any:
|
|
17
|
+
print(
|
|
18
|
+
(
|
|
19
|
+
fname,
|
|
20
|
+
":",
|
|
21
|
+
", ".join(
|
|
22
|
+
"%s=%r" % entry for entry in list(zip(argnames, args)) + list(kwargs.items())
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
return func(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
return echo_func
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def memoize(obj: Callable[..., Any]) -> Callable[..., Any]:
|
|
32
|
+
cache: dict[Any, Any] = {}
|
|
33
|
+
obj.cache = cache # type: ignore[attr-defined]
|
|
34
|
+
|
|
35
|
+
@functools.wraps(obj)
|
|
36
|
+
def memoizer(*args: Any, **kwargs: Any) -> Any:
|
|
37
|
+
if kwargs: # frozenset is used to ensure hashability
|
|
38
|
+
key: Any = (args, frozenset(kwargs.items()))
|
|
39
|
+
else:
|
|
40
|
+
key = args
|
|
41
|
+
if key not in cache:
|
|
42
|
+
cache[key] = obj(*args, **kwargs)
|
|
43
|
+
return cache[key]
|
|
44
|
+
|
|
45
|
+
return memoizer
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class memoized:
|
|
49
|
+
"""Decorator. Caches a function's return value each time it is called.
|
|
50
|
+
If called later with the same arguments, the cached value is returned
|
|
51
|
+
(not reevaluated).
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
func: Callable[..., Any]
|
|
55
|
+
cache: dict[Any, Any]
|
|
56
|
+
|
|
57
|
+
def __init__(self, func: Callable[..., Any]) -> None:
|
|
58
|
+
self.func = func
|
|
59
|
+
self.cache = {}
|
|
60
|
+
|
|
61
|
+
def __call__(self, *args: Any) -> Any:
|
|
62
|
+
if not isinstance(args, collections.abc.Hashable):
|
|
63
|
+
# uncacheable. a list, for instance.
|
|
64
|
+
# better to not cache than blow up.
|
|
65
|
+
return self.func(*args)
|
|
66
|
+
if args in self.cache:
|
|
67
|
+
return self.cache[args]
|
|
68
|
+
else:
|
|
69
|
+
value = self.func(*args)
|
|
70
|
+
self.cache[args] = value
|
|
71
|
+
return value
|
|
72
|
+
|
|
73
|
+
def __repr__(self) -> str:
|
|
74
|
+
"""Return the function's docstring."""
|
|
75
|
+
return self.func.__doc__ or ""
|
|
76
|
+
|
|
77
|
+
def __get__(self, obj: Any, objtype: Any) -> Callable[..., Any]:
|
|
78
|
+
"""Support instance methods."""
|
|
79
|
+
return functools.partial(self.__call__, obj)
|
pyx12/errh_json.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# Copyright
|
|
3
|
+
# John Holland <john@zoner.org>
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# This software is licensed as described in the file LICENSE.txt, which
|
|
7
|
+
# you should have received as part of this distribution.
|
|
8
|
+
#
|
|
9
|
+
######################################################################
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
Generates a JSON document of validation errors.
|
|
13
|
+
Visitor - Visits an error_handler composite (issue #50).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from typing import Any, TextIO
|
|
20
|
+
|
|
21
|
+
import pyx12.error_visitor
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class errh_json_visitor(pyx12.error_visitor.error_visitor):
|
|
25
|
+
"""
|
|
26
|
+
Visit an error_handler composite. Accumulate a nested dict mirroring
|
|
27
|
+
the ISA / GS / ST / segment / element hierarchy and dump it once as
|
|
28
|
+
JSON in visit_root_post.
|
|
29
|
+
|
|
30
|
+
Streaming JSON during the walk would require closing arrays mid-walk;
|
|
31
|
+
the err_handler tree always fits in memory for any X12 document, so
|
|
32
|
+
accumulate-then-dump is the right shape.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
fd: TextIO
|
|
36
|
+
indent: int | None
|
|
37
|
+
doc: dict[str, Any]
|
|
38
|
+
_stack: list[dict[str, Any]]
|
|
39
|
+
_cur_seg: dict[str, Any] | None
|
|
40
|
+
|
|
41
|
+
def __init__(self, fd: TextIO, indent: int | None = 2) -> None:
|
|
42
|
+
"""
|
|
43
|
+
:param fd: target file
|
|
44
|
+
:type fd: file descriptor
|
|
45
|
+
:param indent: json.dump indent argument; pass None for compact output
|
|
46
|
+
:type indent: int | None
|
|
47
|
+
"""
|
|
48
|
+
self.fd = fd
|
|
49
|
+
self.indent = indent
|
|
50
|
+
self.doc = {"interchanges": []}
|
|
51
|
+
self._stack = []
|
|
52
|
+
self._cur_seg = None
|
|
53
|
+
|
|
54
|
+
def visit_root_post(self, errh: Any) -> None:
|
|
55
|
+
"""
|
|
56
|
+
:param errh: Error handler
|
|
57
|
+
:type errh: L{error_handler.err_handler}
|
|
58
|
+
"""
|
|
59
|
+
json.dump(self.doc, self.fd, indent=self.indent, default=str)
|
|
60
|
+
|
|
61
|
+
def visit_isa_pre(self, err_isa: Any) -> None:
|
|
62
|
+
"""
|
|
63
|
+
:param err_isa: ISA error node
|
|
64
|
+
:type err_isa: L{error_handler.err_isa}
|
|
65
|
+
"""
|
|
66
|
+
isa_dict: dict[str, Any] = {
|
|
67
|
+
"isa_trn_set_id": err_isa.isa_trn_set_id,
|
|
68
|
+
"ta1_req": err_isa.ta1_req,
|
|
69
|
+
"orig_date": err_isa.orig_date,
|
|
70
|
+
"orig_time": err_isa.orig_time,
|
|
71
|
+
"cur_line": err_isa.get_cur_line(),
|
|
72
|
+
"errors": [],
|
|
73
|
+
"groups": [],
|
|
74
|
+
}
|
|
75
|
+
self.doc["interchanges"].append(isa_dict)
|
|
76
|
+
self._stack.append(isa_dict)
|
|
77
|
+
|
|
78
|
+
def visit_isa_post(self, err_isa: Any) -> None:
|
|
79
|
+
isa_dict = self._stack[-1]
|
|
80
|
+
isa_dict["errors"] = [
|
|
81
|
+
{"err_cde": cde, "err_str": err_str} for (cde, err_str) in err_isa.errors
|
|
82
|
+
]
|
|
83
|
+
self._stack.pop()
|
|
84
|
+
|
|
85
|
+
def visit_gs_pre(self, err_gs: Any) -> None:
|
|
86
|
+
gs_dict: dict[str, Any] = {
|
|
87
|
+
"gs_control_num": err_gs.gs_control_num,
|
|
88
|
+
"fic": err_gs.fic,
|
|
89
|
+
"vriic": err_gs.vriic,
|
|
90
|
+
"ack_code": err_gs.ack_code,
|
|
91
|
+
"st_count_orig": err_gs.st_count_orig,
|
|
92
|
+
"st_count_recv": err_gs.st_count_recv,
|
|
93
|
+
"cur_line": err_gs.get_cur_line(),
|
|
94
|
+
"errors": [],
|
|
95
|
+
"transactions": [],
|
|
96
|
+
}
|
|
97
|
+
self._stack[-1]["groups"].append(gs_dict)
|
|
98
|
+
self._stack.append(gs_dict)
|
|
99
|
+
|
|
100
|
+
def visit_gs_post(self, err_gs: Any) -> None:
|
|
101
|
+
gs_dict = self._stack[-1]
|
|
102
|
+
gs_dict["errors"] = [
|
|
103
|
+
{"err_cde": cde, "err_str": err_str} for (cde, err_str) in err_gs.errors
|
|
104
|
+
]
|
|
105
|
+
self._stack.pop()
|
|
106
|
+
|
|
107
|
+
def visit_st_pre(self, err_st: Any) -> None:
|
|
108
|
+
st_dict: dict[str, Any] = {
|
|
109
|
+
"trn_set_id": err_st.trn_set_id,
|
|
110
|
+
"trn_set_control_num": err_st.trn_set_control_num,
|
|
111
|
+
"vriic": err_st.vriic,
|
|
112
|
+
"ack_code": err_st.ack_code,
|
|
113
|
+
"cur_line": err_st.get_cur_line(),
|
|
114
|
+
"errors": [],
|
|
115
|
+
"segments": [],
|
|
116
|
+
}
|
|
117
|
+
self._stack[-1]["transactions"].append(st_dict)
|
|
118
|
+
self._stack.append(st_dict)
|
|
119
|
+
|
|
120
|
+
def visit_st_post(self, err_st: Any) -> None:
|
|
121
|
+
st_dict = self._stack[-1]
|
|
122
|
+
st_dict["errors"] = [
|
|
123
|
+
{"err_cde": cde, "err_str": err_str} for (cde, err_str) in err_st.errors
|
|
124
|
+
]
|
|
125
|
+
self._stack.pop()
|
|
126
|
+
|
|
127
|
+
def visit_seg(self, err_seg: Any) -> None:
|
|
128
|
+
seg_dict: dict[str, Any] = {
|
|
129
|
+
"seg_id": err_seg.seg_id,
|
|
130
|
+
"seg_count": err_seg.seg_count,
|
|
131
|
+
"pos": err_seg.pos,
|
|
132
|
+
"name": err_seg.name,
|
|
133
|
+
"ls_id": err_seg.ls_id,
|
|
134
|
+
"cur_line": err_seg.get_cur_line(),
|
|
135
|
+
"errors": [
|
|
136
|
+
{"err_cde": cde, "err_str": err_str, "err_val": err_val}
|
|
137
|
+
for (cde, err_str, err_val) in err_seg.errors
|
|
138
|
+
],
|
|
139
|
+
"elements": [],
|
|
140
|
+
}
|
|
141
|
+
self._stack[-1]["segments"].append(seg_dict)
|
|
142
|
+
self._cur_seg = seg_dict
|
|
143
|
+
|
|
144
|
+
def visit_ele(self, err_ele: Any) -> None:
|
|
145
|
+
# err_ele.accept is only invoked from err_seg.accept (ISA / GS / ST
|
|
146
|
+
# accept methods do not walk their .elements list), so a non-None
|
|
147
|
+
# _cur_seg is guaranteed at this point.
|
|
148
|
+
if self._cur_seg is None:
|
|
149
|
+
return
|
|
150
|
+
ele_dict: dict[str, Any] = {
|
|
151
|
+
"ele_pos": err_ele.ele_pos,
|
|
152
|
+
"subele_pos": err_ele.subele_pos,
|
|
153
|
+
"repeat_pos": err_ele.repeat_pos,
|
|
154
|
+
"ele_ref_num": err_ele.ele_ref_num,
|
|
155
|
+
"name": err_ele.name,
|
|
156
|
+
"errors": [
|
|
157
|
+
{"err_cde": cde, "err_str": err_str, "err_val": err_val}
|
|
158
|
+
for (cde, err_str, err_val) in err_ele.errors
|
|
159
|
+
],
|
|
160
|
+
}
|
|
161
|
+
self._cur_seg["elements"].append(ele_dict)
|