Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ TestResults/
obj/
testenv/
Backup/pyx12.sln
*.htm
*.htm
*~
.idea/
40 changes: 28 additions & 12 deletions pyx12/map_if.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,14 +986,14 @@ def is_valid(self, seg_data, errh):
ref_des = '%02i' % (i + 1)
comp_data = seg_data.get(ref_des)
subele_count = child_node.get_child_count()
if seg_data.ele_len(ref_des) > subele_count and child_node.usage != 'N':
subele_node = child_node.get_child_node_by_idx(
subele_count + 1)
if seg_data.ele_max_len(ref_des) > subele_count and child_node.usage != 'N':
err_str = 'Too many sub-elements in composite "%s" (%s)' % \
(subele_node.name, subele_node.refdes)
(child_node.name, child_node.refdes)
err_value = seg_data.get_value(ref_des)
errh.ele_error('3', err_str, err_value, ref_des)
valid &= child_node.is_valid(comp_data, errh)
valid = False
else:
valid &= child_node.is_valid(comp_data, errh)
elif child_node.is_element():
# Validate Element
if i == 1 and seg_data.get_seg_id() == 'DTP' \
Expand Down Expand Up @@ -1496,17 +1496,33 @@ def is_valid(self, comp_data, errh):
errh.ele_error('5', err_str, None, self.refdes)
return False

if len(comp_data) > self.get_child_count():
if comp_data.max_len() > self.get_child_count():
err_str = 'Too many sub-elements in composite "%s" (%s)' % (
self.name, self.refdes)
errh.ele_error('3', err_str, None, self.refdes)
valid = False
for i in range(min(len(comp_data), self.get_child_count())):
valid &= self.get_child_node_by_idx(i).is_valid(comp_data[i], errh)
for i in range(min(len(comp_data), self.get_child_count()), self.get_child_count()):
if i < self.get_child_count():
#Check missing required elements
valid &= self.get_child_node_by_idx(i).is_valid(None, errh)
if self.repeat < comp_data.get_repetitions():
err_str = 'Too many repetitions in composite "%s" (%s)' % (self.name, self.refdes)
errh.ele_error('12', err_str, None, self.refdes)
valid = False
idx = 0
for i in range(min(len(comp_data), self.get_child_count() * self.repeat)):
# repeating, reset the idx counter
if comp_data[i] == comp_data.repetition_term:
for j in range(min(idx, self.get_child_count()), self.get_child_count()):
if j < self.get_child_count():
# Check missing required elements
valid &= self.get_child_node_by_idx(j).is_valid(None, errh)
idx = 0
continue
valid &= self.get_child_node_by_idx(idx).is_valid(comp_data[i], errh)
idx += 1
# validate the non-repeating segments (or last repeat seg)
for j in range(min(idx, self.get_child_count()), self.get_child_count()):
if j < self.get_child_count():
# Check missing required elements
valid &= self.get_child_node_by_idx(j).is_valid(None, errh)

return valid

def is_composite(self):
Expand Down
143 changes: 125 additions & 18 deletions pyx12/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@

"""
Implements an interface to a x12 segment.

A segment is comprised of a segment identifier and a sequence of elements.
An element can be a simple element or a composite. A simple element is
treated as a composite element with one sub-element.

All indexing is zero based.
"""
import re
Expand All @@ -24,6 +22,7 @@

rec_seg_id = re.compile('^[A-Z][A-Z0-9]{1,2}$', re.S)


class Element(object):
"""
Holds a simple element, which is just a simple string.
Expand All @@ -33,9 +32,9 @@ def __init__(self, ele_str):
"""
@param ele_str: 1::2
@type ele_str: string

"""
self.value = ele_str if ele_str is not None else ''
self.repetitions = 1

def __eq__(self, other):
if isinstance(other, Element):
Expand Down Expand Up @@ -69,12 +68,24 @@ def __repr__(self):
"""
return self.value

def max_len(self):
"""
@rtype 1
"""
return 1

def format(self):
"""
@rtype: string
"""
return self.value

def format_list(self):
"""
@rtype: list(string)
"""
return [self.value]

def get_value(self):
"""
@rtype: string
Expand Down Expand Up @@ -109,8 +120,12 @@ def is_empty(self):
else:
return True

def get_repetitions(self):
return self.repetitions

# return ''.join([`num` for num in xrange(loop_count)])
# def has_invalid_character(self,
# def has_invalid_character(self,


class Composite(object):
"""
Expand All @@ -120,7 +135,7 @@ class Composite(object):
# Attributes:

# Operations
def __init__(self, ele_str, subele_term=None):
def __init__(self, ele_str, subele_term=None, repetition_term=None):
"""
@type ele_str: string
@raise EngineError: If a terminator is None and no default
Expand All @@ -129,12 +144,29 @@ def __init__(self, ele_str, subele_term=None):
raise EngineError('The sub-element terminator must be a single character, is %s' % (subele_term))
self.subele_term = subele_term
self.subele_term_orig = subele_term
self.repetition_term = repetition_term
if ele_str is None:
raise EngineError('Element string is None')
members = ele_str.split(self.subele_term)

if self.repetition_term:
reps = ele_str.split(self.repetition_term)
self.repetitions = len(reps)
else:
self.repetitions = 1

self.elements = []
for elem in members:
self.elements.append(Element(elem))
# there's at least 1 repetition
if self.repetitions > 1:
for rep in reps:
members = rep.split(self.subele_term)
for elem in members:
self.elements.append(Element(elem))
self.elements.append(self.repetition_term)
self.elements.pop()
else:
members = ele_str.split(self.subele_term)
for elem in members:
self.elements.append(Element(elem))

def __eq__(self, other):
if isinstance(other, Composite):
Expand Down Expand Up @@ -187,10 +219,23 @@ def __repr__(self):
"""
return self.format(self.subele_term)

def max_len(self):
"""
returns the maximum length for a repitition
@retype: int
"""
temp_list = list()
temp_list.append([])
for elem in self.elements:
if elem == self.repetition_term:
temp_list.append([])
else:
temp_list[-1].append(elem)
return max(map(len, temp_list))

def format(self, subele_term=None):
"""
Format a composite

@return: string
@raise EngineError: If terminator is None and no default
"""
Expand All @@ -201,7 +246,48 @@ def format(self, subele_term=None):
for i in range(len(self.elements) - 1, -1, -1):
if not self.elements[i].is_empty():
break
return subele_term.join([Element.__repr__(x) for x in self.elements[:i + 1]])
res = []
for itr, x in enumerate(self.elements[:i + 1]):
if isinstance(x, str):
res.append(x)
else:
res.append(x.__repr__())
if itr + 1 < len(self.elements) and not isinstance(self.elements[itr+1], str):
res.append(subele_term)
if itr+1 < len(self.elements) and self.elements[itr+1].is_empty():
res.pop()
return ''.join(res)

def format_list(self, subele_term=None):
"""
Format a composite and return a tuple of repetitions
@return: list(string)
@raise EngineError: If terminator is None and no default
"""
if subele_term is None:
subele_term = self.subele_term
if subele_term is None:
raise EngineError('subele_term is None')
for i in range(len(self.elements) - 1, -1, -1):
if not self.elements[i].is_empty():
break
res = []
rep = []
for itr, x in enumerate(self.elements[:i + 1]):
if isinstance(x, str):
if itr+1 < len(self.elements) and self.elements[itr+1].is_empty():
rep.pop()
res.append(''.join(rep))
rep = []
else:
rep.append(x.__repr__())
if itr + 1 < len(self.elements) and not isinstance(self.elements[itr+1], str):
rep.append(subele_term)
if len(rep) > 0:
if itr+1 < len(self.elements) and self.elements[itr+1].is_empty():
rep.pop()
res.append(''.join(rep))
return res

def get_value(self):
"""
Expand All @@ -223,7 +309,7 @@ def is_composite(self):
"""
@rtype: boolean
"""
if len(self.elements) > 1:
if self.max_len() > 1:
return True
else:
return False
Expand All @@ -232,7 +318,7 @@ def is_element(self):
"""
@rtype: boolean
"""
if len(self.elements) == 1:
if self.max_len() == 1:
return True
else:
return False
Expand All @@ -252,6 +338,9 @@ def values_iterator(self):
subele_ord = '{comp}'.format(comp=j+1)
yield (subele_ord, self.elements[j].get_value())

def get_repetitions(self):
return self.repetitions


class Segment(object):
"""
Expand Down Expand Up @@ -286,7 +375,7 @@ def __init__(self, seg_str, seg_term, ele_term, subele_term, repetition_term='^'
#guarantee subele_term will not be matched
self.elements.append(Composite(ele, ele_term))
else:
self.elements.append(Composite(ele, subele_term))
self.elements.append(Composite(ele, subele_term, repetition_term))

def __eq__(self, other):
if isinstance(other, Segment):
Expand Down Expand Up @@ -324,7 +413,6 @@ def __repr__(self):
def append(self, val):
"""
Append a composite to the segment

@param val: String value of composite
@type val: string
"""
Expand All @@ -349,7 +437,6 @@ def _parse_refdes(self, ref_des):
- a composite: TST03 where TST03 is a composite
- a sub-element: TST03-2
- or any of the above with the segment ID omitted (02, 03, 03-1)

@param ref_des: X12 Reference Designator
@type ref_des: string
@rtype: tuple(ele_idx, subele_idx)
Expand Down Expand Up @@ -396,6 +483,17 @@ def get_value(self, ref_des):
else:
return comp1.format()

def get_values_list(self, ref_des):
"""
@param ref_des: X12 Reference Designator
@type ref_des: string
"""
comp1 = self.get(ref_des)
if comp1 is None:
return None
else:
return comp1.format_list()

def get_value_by_ref_des(self, ref_des):
"""
@param ref_des: X12 Reference Designator
Expand All @@ -408,7 +506,6 @@ def set(self, ref_des, val):
"""
Set the value of an element or subelement identified by the
Reference Designator

@param ref_des: X12 Reference Designator
@type ref_des: string
@param val: New value
Expand Down Expand Up @@ -457,6 +554,16 @@ def ele_len(self, ref_des):
ele_idx = self._parse_refdes(ref_des)[0]
return len(self.elements[ele_idx])

def ele_max_len(self, ref_des):
"""
@param ref_des: X12 Reference Designator
@type ref_des: string
@return: max number of sub-elements in an element or composite repititon
@rtype: int
"""
ele_idx = self._parse_refdes(ref_des)[0]
return self.elements[ele_idx].max_len()

def set_seg_term(self, seg_term):
"""
@param seg_term: Segment terminator
Expand Down Expand Up @@ -533,7 +640,7 @@ def is_empty(self):
def is_seg_id_valid(self):
"""
Is the Segment identifier valid?
EBNF:
EBNF:
<seg_id> ::= <letter_or_digit> <letter_or_digit> [<letter_or_digit>]
@rtype: boolean
"""
Expand Down Expand Up @@ -565,4 +672,4 @@ def values_iterator(self):
if not self.elements[i].is_empty():
ele_ord = '{idx:0>2}'.format(idx=i+1)
refdes = '{segid}{ele_ord}'.format(segid=self.seg_id, ele_ord=ele_ord)
yield (refdes, ele_ord, None, self.elements[i].get_value())
yield (refdes, ele_ord, None, self.elements[i].get_value())
Loading