document.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. """
  2. This file encapsulates the functions needed to build a document
  3. """
  4. import numpy as np
  5. import copy
  6. class Builder:
  7. __doc__ = """
  8. This class is intended to create and manipulate objects
  9. :merge The class merges two objects and accounts for attributes that are lists
  10. :parent returns the parent for a given object
  11. """
  12. def __init__(self,**_args):
  13. self._last = {}
  14. self._plugins = copy.deepcopy(_args['plugins'])
  15. self._parents = copy.deepcopy(_args['parents'])
  16. self._loop = {}
  17. def reset (self):
  18. self._last = {}
  19. self._loop = {}
  20. def parent(self,**_args):
  21. """
  22. This function returns the parent item of an object
  23. :meta meta data of a decorated/annotated function
  24. """
  25. _meta = _args['meta']
  26. # _item = None
  27. if _meta['parent'] :
  28. _id = _meta['parent']
  29. if _id :
  30. return self._last[_id] if _id in self._last else None
  31. return None
  32. # if _id in self._parents :
  33. # self._last[_id] =
  34. # if 'parent' in _meta : #hasattr(_meta,'parent'):
  35. # _hasField = 'field' in _meta
  36. # _hasParent= _meta['element'] in self._parents
  37. # if _hasField and _hasParent: #_meta.element in self._parents and hasattr(_meta,'field'):
  38. # self._last = _item
  39. # pass
  40. # else:
  41. # for key in self._parents :
  42. # if _meta['element'] in self._parents[key] :
  43. # _ikey = list(self._last.keys())[0]
  44. # _oldinfo = self._last[_ikey]
  45. # if type(_oldinfo) != dict :
  46. # #
  47. # # Only applicable against a dictionary not a list (sorry)
  48. # pass
  49. # else:
  50. # _item = {_ikey: self.merge(_oldinfo,_item)}
  51. # break
  52. # pass
  53. # return _item
  54. def count(self,_element):
  55. if _element not in self._loop :
  56. self._loop[_element] = 0
  57. self._loop[_element] += 1
  58. def pointer(self,**_args):
  59. """
  60. This function returns a pointer associated with a row element
  61. @TODO: Make sure we know what kind of file we are processing (it would help suppress the loop)
  62. """
  63. _id = _args['row'][0] if 'row' in _args else _args['element']
  64. _filetype = _args['x12']
  65. _pointer = None
  66. if _id in self._plugins[_filetype] :
  67. _pointer = self._plugins[_filetype][_id]
  68. else:
  69. for _x12 in self._plugins :
  70. if _id in self._plugins[_x12] :
  71. _pointer = self._plugins[_x12][_id]
  72. break
  73. return _pointer
  74. def field(self,**_args) :
  75. _row = _args['row']
  76. _meta= _args['meta']
  77. _field = None
  78. if _meta['parent'] :
  79. _field = self.parent(meta=_meta)['field']
  80. if 'field' in _meta or 'container' in _meta :
  81. _field = _meta['field'] if 'field' in _meta else _meta['container']
  82. if 'anchor' in _meta :
  83. _anchor = _meta['anchor']
  84. for key in _anchor :
  85. if key == _row[1].strip() :
  86. _field = _anchor[key]
  87. break
  88. return _field
  89. def merge (self,_x,_y):
  90. """
  91. This function will merge two objects _x, _y
  92. """
  93. _zcols = list(set(_x.keys()) & set(_y.keys())) #--common columns
  94. if _zcols :
  95. _out = dict(_x,**{})
  96. for _key in list(_y.keys()) :
  97. if _key not in _zcols and _key:
  98. _out[_key] = _y[_key]
  99. else:
  100. if type(_out[_key]) == list :
  101. for value in _y[_key] :
  102. if value not in _out[_key] :
  103. _out[_key].append(value)
  104. # _out[_key] += _y[_key]
  105. elif type(_out[_key]) == dict:
  106. _out[_key] = dict(_out[_key],**_y[_key])
  107. else:
  108. _out[_key] = _y[_key]
  109. return _out
  110. else:
  111. return dict(_x,**_y)
  112. def parse (self,**_args):
  113. """
  114. This function will perform parsing on behalf of the plugin by relying on map function
  115. :row raw x12 row
  116. :meta meta data of the plugin function
  117. """
  118. #-- Loop Markers
  119. _row = _args['row']
  120. _map = _args['meta']['map']
  121. # _map = self.pointer(row=_row).meta['map']
  122. _index = list(_map.keys())
  123. _columns = [] #[_map[_id] for _id in _index ]
  124. for _id in _index :
  125. _name = _map[_id]
  126. if type(_name) == list :
  127. _columns += _name
  128. _i = _index.index(_id)
  129. _index = (_index[:_i] + np.repeat(_index[_i], len(_name)).tolist()+_index[_i+1:])
  130. else:
  131. _columns.append(_name)
  132. _info = {}
  133. _index = np.array(_index).astype(int)
  134. # _document = _args['document']
  135. if np.max(_index) > len(_row) -1 :
  136. _delta = 1 + np.max(_index) - len(_row)
  137. _row = _row + np.repeat('',_delta).tolist()
  138. _row = np.array(_row)
  139. try:
  140. _info = dict(zip(_columns,_row[_index].tolist()))
  141. except Exception as e:
  142. # print (_row)
  143. # print ( e)
  144. pass
  145. return _info
  146. def meta (self,**_args):
  147. _row = _args['row']
  148. _id = _row[0]
  149. _meta = None
  150. for key in self._plugins :
  151. _items = self._plugins[key]
  152. if _id in _items :
  153. _meta = (_items[_id].meta)
  154. break
  155. return _meta
  156. def update(self,**_args):
  157. _element = _args['row'][0]
  158. if _element in self._parents :
  159. _meta = self.meta(row=_args['row'])
  160. if 'field' not in _meta :
  161. _field = self.field(row=_args['row'],meta=_meta)
  162. else:
  163. _field = _meta['field']
  164. self._last[_element] = {'data':_args['data'],'field':_field}
  165. def bind(self,**_args):
  166. """
  167. This function is intended to make an object out of an element
  168. :row raw row of x12
  169. :document object that is the document
  170. """
  171. _row = _args['row']
  172. _filetype = _args['x12']
  173. _id = _row[0]
  174. self.count(_id)
  175. _pointer = self.pointer(row=_row,x12=_filetype)
  176. _parent = None
  177. _data = {}
  178. # _document = _args['document']
  179. if not _pointer :
  180. return None,None
  181. #
  182. # Should we use the built-in parser or not
  183. if _pointer and 'map' in _pointer.meta :
  184. _data = self.parse(row=_row,meta=_pointer.meta)
  185. #
  186. # This function will be used as formatter (at least)
  187. # We will also insure that the current element is not the last one
  188. _out = _pointer(row=_row,data=_data, meta=_pointer.meta)
  189. _data = _data if _out is None else _out
  190. self.update(row = _row, data=_data) #-- If this element is considered a parent, we store it
  191. return _data, _pointer.meta
  192. def build (self,**_args):
  193. """
  194. This function attemps to place a piece of data within a document
  195. """
  196. _meta = _args['meta']
  197. _data = _args['data']
  198. _row = _args['row']
  199. _document = _args['document']
  200. # if _meta['parent'] :
  201. # _field = self.parent(meta=_meta)['field']
  202. # elif 'field' in _meta :
  203. # _field = _meta['field']
  204. # elif 'container' in _meta :
  205. # _field = _meta['container']
  206. # if type(_document[_field]) != list :
  207. # _data = self.merge(_document[_field],_data)
  208. # _document[_field] = []
  209. # elif 'anchor' in _meta:
  210. # _field = self.field(row=_row,meta=_meta)
  211. # else:
  212. # _field = None
  213. _field = self.field(meta=_meta,row=_row)
  214. if _field :
  215. if 'container' in _meta and type(_document[_field]) != list :
  216. _document[_field] = []
  217. if _field and _document:
  218. if _field not in _document :
  219. _document[_field] =_data
  220. else:
  221. if 'container' in _meta :
  222. _document[_field].append(_data)
  223. else:
  224. _document[_field] = self.merge(_document[_field],_data)
  225. else:
  226. if not _field and 'anchor' in _meta :
  227. #
  228. # This is an unusual situation ...
  229. pass
  230. _document = self.merge(_document,_data)
  231. return _document