######################################################################
# Copyright
# John Holland <john@zoner.org>
# All rights reserved.
#
# This software is licensed as described in the file LICENSE.txt, which
# you should have received as part of this distribution.
#
######################################################################
"""
Generates HTML error output
"""
from __future__ import annotations
import html
import logging
import time
from typing import Any, TextIO
# Intrapackage imports
import pyx12.segment
[docs]
logger = logging.getLogger("pyx12.error_html")
logger.setLevel(logging.DEBUG)
# logger.setLevel(logging.ERROR)
[docs]
class error_html:
""" """
def __init__(
self,
errh: Any,
fd: TextIO,
term: tuple[Any, ...] = ("~", "*", "~", "\n"),
) -> None:
"""
:param fd: target file
:type fd: file descriptor
:param term: tuple of x12 terminators used
:type term: tuple(string, string, string, string)
Bug: GS errors are re-printing at the GE level
"""
self.errh = errh
self.fd = fd
self.seg_term = term[0]
self.ele_term = term[1]
self.subele_term = term[2]
self.eol = ""
self.last_line = 0
self.loop_info = None
[docs]
def loop(self, loop_node: Any) -> None:
if loop_node.type != "wrapper":
# self.gen_info('Loop %s: %s' % (loop_node.id, loop_node.name))
self.loop_info = "Loop %s: %s" % (loop_node.id, loop_node.name)
[docs]
def gen_info(self, info_str: str) -> None:
""" """
self.fd.write('<span class="info"> %s</span><br />\n' % (info_str))
[docs]
def gen_seg(self, seg_data: pyx12.segment.Segment, src: Any, err_node_list: list[Any]) -> None:
"""
Find error seg for this segment.
Find any skipped error values.
ID pos of bad value.
:param seg_data: data segment instance
"""
cur_line = src.cur_line
# while errh
ele_pos_map: dict[int, int] = {}
for err_node in err_node_list:
for ele in err_node.elements:
ele_pos_map[ele.ele_pos] = ele.subele_pos
t_seg: list[Any] = [] # list of formatted elements
# seg_data.format_ele_list(t_seg)
for i in range(1, len(seg_data) + 1):
if seg_data.is_composite(ref_des="%02i" % (i)):
# if seg_data.get_seg_id()=='CLM': pdb.set_trace()
t_seg.append([])
for j in range(1, seg_data.ele_len("%02i" % (i)) + 1):
ref_des = "%02i-%i" % (i, j)
ele_str = escape_html_chars(seg_data.get_value(ref_des))
if i in ele_pos_map.keys() and ele_pos_map[i] == j:
ele_str = self._wrap_ele_error(ele_str)
t_seg[-1].append(ele_str)
else:
ref_des = "%02i" % (i)
ele_str = escape_html_chars(seg_data.get_value(ref_des))
if i in ele_pos_map.keys():
ele_str = self._wrap_ele_error(ele_str)
t_seg.append(ele_str)
for err_node in err_node_list:
# for err_tuple in err_node.errors:
for err_tuple in err_node.get_error_list(seg_data.get_seg_id(), True):
err_cde = err_tuple[0]
err_str = err_tuple[1]
if err_cde == "3":
self.fd.write(
'<span class="error"> %s (Segment Error Code: %s)</span><br />\n'
% (err_str, err_cde)
)
if self.loop_info:
self.gen_info(self.loop_info)
self.loop_info = None
self.fd.write(
'<span class="seg">%i: %s</span><br />\n'
% (cur_line, self._seg_str(seg_data.get_seg_id(), t_seg))
)
for err_node in err_node_list:
for err_tuple in err_node.get_error_list(seg_data.get_seg_id(), False):
# for err_tuple in err_node.errors:
err_cde = err_tuple[0]
err_str = err_tuple[1]
if err_cde != "3":
self.fd.write(
'<span class="error"> %s (Segment Error Code: %s)</span><br />\n'
% (err_str, err_cde)
)
for ele in err_node.elements:
for err_cde, err_str, err_val in ele.get_error_list(seg_data.get_seg_id(), False):
# for (err_cde, err_str, err_val) in ele.errors:
if not (seg_data.get_seg_id() == "GE" and "GS" in err_str): # Ugly hack
self.fd.write(
'<span class="error"> %s (Element Error Code: %s)</span><br />\n'
% (err_str, err_cde)
)
def _seg_str(self, seg_id: str | None, ele_list: list[Any]) -> str:
"""
:param ele_list: list of formatted elements
:rtype: string
"""
return (
(seg_id or "")
+ self.ele_term
+ seg_str(ele_list, self.seg_term, self.ele_term, self.subele_term, self.eol)
)
def _wrap_ele_error(self, str1: str | None) -> str:
"""
:rtype: string
"""
return '<span class="ele_err">%s</span>' % (str1)
[docs]
def seg_str(
seg: list[Any],
seg_term: str,
ele_term: str,
subele_term: str,
eol: str = "",
) -> str:
"""
Join a list of elements
:param seg: List of elements
:type seg: list[string|list[string]]
:param seg_term: Segment terminator character
:type seg_term: string
:param ele_term: Element terminator character
:type ele_term: string
:param subele_term: Sub-element terminator character
:type subele_term: string
:param eol: End of line character
:type eol: string
:return: formatted segment
:rtype: string
"""
# if None in seg:
# logger.debug(seg)
tmp: list[str] = []
for a in seg:
if type(a) is list:
tmp.append(subele_term.join(a))
else:
tmp.append(a)
return "%s%s%s" % (ele_term.join(tmp), seg_term, eol)
[docs]
def escape_html_chars(str_val: str | None) -> str | None:
"""
Escape special HTML characters, including quotes, so the result is safe in
both element text and attribute contexts. Spaces are replaced with
after escaping to preserve column alignment in the rendered report.
"""
if str_val is None:
return None
return html.escape(str_val, quote=True).replace(" ", " ")