Skip to content

Commit 1385e88

Browse files
committed
[REF] refactor code and introduce parsing class
1 parent 4f948cd commit 1385e88

File tree

2 files changed

+287
-300
lines changed

2 files changed

+287
-300
lines changed

cfonb/parser/common.py

Lines changed: 267 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# python import
22
import math
3+
import re
34

45
# blank only rule
56
G__ = r'( {%d})'
@@ -48,26 +49,285 @@ class Row(dict):
4849
"""Generic row object to manage bank file parsing, compare and reading.
4950
"""
5051

51-
def __init__(self, re, keys, line):
52+
def __init__(self, line):
5253
"""Parses flat line according re rules, line and additional info.
5354
5455
:param re: regular expression to match with
5556
:param keys: keys values to build dict and object variables for the row
5657
:param line: the flat line to be parsed
5758
"""
5859
# do re match
59-
match = re.match(line)
60+
parser = Parser.get_parser(line[0:2])
61+
self.update(parser().parse(line))
62+
63+
def __getattr__(self, attr):
64+
return self[attr]
65+
66+
def __setattr__(self, attr, value):
67+
self[attr] = value
68+
69+
70+
class Parser(object):
71+
""" Parser method for reading bank statement line """
72+
@classmethod
73+
def get_parser(cls, key):
74+
for sub_cls in cls.__subclasses__():
75+
if sub_cls._code == key:
76+
return sub_cls
77+
import pdb; pdb.set_trace()
78+
raise Exception('No class found')
79+
80+
def __init__(self):
81+
self._regex
82+
regex = r''
83+
keys = []
84+
for key, value in self._regex:
85+
regex += value
86+
keys += [key]
87+
self.re = re.compile(regex + r'$')
88+
self.keys = keys
89+
90+
def parse(self, line):
91+
if line[-1] == "\n":
92+
line = line[:-1]
93+
match = self.re.match(line)
6094
# re check
6195
if match is None:
6296
raise ParsingError('line is invalid: "%s"' % line)
6397
else:
64-
self.update(dict(zip(keys, list(match.groups()))))
98+
return dict(zip(self.keys, list(match.groups())))
6599

66-
def __getattr__(self, attr):
67-
return self[attr]
68100

69-
def __setattr__(self, attr, value):
70-
self[attr] = value
101+
# http://www.cfonb.org/Web/cfonb/cfonbmain.nsf/DocumentsByIDWeb/7JSHS5/$File/7-8%20Evolution%20Releve%20Comptes%20120%20caracteres%20operations%20virement%20et%20prelevement%20sepa%20V2_0_2010_03.pdf
102+
103+
class ParserContent01(Parser):
104+
_code = '01'
105+
_regex = [
106+
('record_code', '(01)' ),
107+
('bank_code', G_N % 5 ),
108+
('_1', G__ % 4 ),
109+
('desk_code', G_N % 5 ),
110+
('currency_code', G_A_ % 3 ),
111+
('nb_of_dec', G_N_ % 1 ),
112+
('_2', G__ % 1 ),
113+
('account_nb', G_AN % 11),
114+
('_3', G__ % 2 ),
115+
('prev_date', G_N % 6 ),
116+
('_4', G__ % 50),
117+
('prev_amount', G_AMT ),
118+
('_5', G__ % 16),
119+
]
120+
121+
122+
class ParserContent04(Parser):
123+
_code = '04'
124+
_regex = [
125+
('record_code', '(04)' ),
126+
('bank_code', G_N % 5 ),
127+
('inernal_code', G_AN % 4 ),
128+
('desk_code', G_N % 5 ),
129+
('currency_code', G_A_ % 3 ),
130+
('nb_of_dec', G_N_ % 1 ),
131+
('_1', G_AN_ % 1 ),
132+
('account_nb', G_AN % 11),
133+
('operation_code', G_AN % 2 ),
134+
('operation_date', G_N % 6 ),
135+
('reject_code', G_N_ % 2 ),
136+
('value_date', G_N % 6 ),
137+
('label', G_ALL % 31),
138+
('_2', G_AN_ % 2 ),
139+
('reference', G_AN % 7 ),
140+
('exempt_code', G_AN_ % 1 ),
141+
('_3', G_AN_ % 1 ),
142+
('amount', G_AMT ),
143+
('_4:', G_AN_ % 16),
144+
]
145+
146+
147+
class ParserContent05(Parser):
148+
_code = '05'
149+
_regex = [
150+
('record_code', '(05)'),
151+
('bank_code', G_N % 5 ),
152+
('inernal_code', G_AN % 4 ),
153+
('desk_code', G_N % 5 ),
154+
('currency_code', G_A_ % 3 ),
155+
('nb_of_dec', G_N_ % 1 ),
156+
('_1', G_AN_ % 1 ),
157+
('account_nb', G_AN % 11),
158+
('operation_code', G_AN % 2 ),
159+
('operation_date', G_N % 6 ),
160+
('_2', G__ % 5 ),
161+
('qualifier', G_AN % 3 ),
162+
('additional_info', G_ALL % 70),
163+
('_3', G__ % 2 ),
164+
]
165+
166+
def parse(self, line):
167+
result = super(ParserContent05, self).parse(line)
168+
parser = Parser.get_parser(result['qualifier'])
169+
new_result = parser().parse(result['additional_info'])
170+
return new_result
171+
172+
173+
class RoxContent07(Parser):
174+
_code = '07'
175+
_regex = [
176+
('record_code', '(07)' ),
177+
('bank_code', G_N % 5 ),
178+
('_1', G__ % 4 ),
179+
('desk_code', G_N % 5 ),
180+
('currency_code', G_A_ % 3 ),
181+
('nb_of_dec', G_N_ % 1 ),
182+
('_2', G__ % 1 ),
183+
('account_nb', G_AN % 11),
184+
('_3', G__ % 2 ),
185+
('next_date', G_N % 6 ),
186+
('_4', G__ % 50),
187+
('next_amount', G_AMT ),
188+
('_5', G__ % 16),
189+
]
190+
191+
192+
class ParserLIB(Parser):
193+
_code = "LIB"
194+
_regex = [
195+
('label', G_ALL %70),
196+
]
197+
198+
199+
class ParserNPY(Parser):
200+
_code = "NPY"
201+
_regex = [
202+
('debtor_name', G_ALL %70),
203+
]
204+
205+
206+
class ParserIPY(Parser):
207+
_code = 'IPY'
208+
_regex = [
209+
('debtor_id', G_ALL %35),
210+
('debtor_id_type', G_ALL %35),
211+
]
212+
213+
214+
class ParserNBE(Parser):
215+
_code = 'NBE'
216+
_regex = [
217+
('creditor_name', G_ALL %70),
218+
]
219+
220+
221+
class ParserIBE(Parser):
222+
_code = 'IBE'
223+
_regex = [
224+
('creditor_id', G_ALL %35),
225+
('creditor_id_type', G_ALL %35),
226+
]
227+
228+
229+
class ParserNPO(Parser):
230+
_code = 'NPO'
231+
_regex = [
232+
('ultimate_debtor_name', G_ALL %70),
233+
]
234+
235+
236+
class ParserIPO(Parser):
237+
_code = 'IPO'
238+
_regex = [
239+
('ultimate_debtor_id', G_ALL %35),
240+
('ultimate_debtor_type', G_ALL %35),
241+
]
242+
243+
244+
class ParserNBU(Parser):
245+
_code = 'NBU'
246+
_regex = [
247+
('ultimate_creditor_name', G_ALL %70),
248+
]
249+
250+
251+
class ParserIBU(Parser):
252+
_code = 'IBU'
253+
_regex = [
254+
('ultimate_creditor_id', G_ALL %35),
255+
('ultimate_creditor_type', G_ALL %35),
256+
]
257+
258+
259+
class ParserLCC(Parser):
260+
_code = 'LCC'
261+
_regex = [
262+
('remittance_information_1', G_ALL %70),
263+
]
264+
265+
266+
class ParserLC2(Parser):
267+
_code = 'LC2'
268+
_regex = [
269+
('remittance_information_2', G_ALL %70),
270+
]
271+
272+
273+
class ParserLCS(Parser):
274+
_code = 'LCS'
275+
_regex = [
276+
('creditor_ref_information', G_ALL %70),
277+
]
278+
279+
280+
class ParserRCN(Parser):
281+
_code = 'RCN'
282+
_regex = [
283+
('end2end_identification', G_ALL %35),
284+
('purpose', G_ALL %35),
285+
]
286+
287+
288+
class ParserRCN(Parser):
289+
_code = 'RCN'
290+
_regex = [
291+
('payment_infor_id', G_ALL %35),
292+
('instruction_id', G_ALL %35),
293+
]
294+
295+
296+
#Specific to bank transfers
297+
class ParserMMO(Parser):
298+
_code = 'MMO'
299+
_regex = [
300+
('currency_code', G_AN %3),
301+
('nb_of_dec_amount', G_N %1),
302+
('equivalent_amount', G_N_ %14),
303+
('nb_of_dec_exchange_rate', G_N_ %2),
304+
('exchange_rate', G_N_ %11),
305+
('_', G_AN_ %39)
306+
]
307+
308+
309+
class ParserCBE(Parser):
310+
_code = 'CBE'
311+
_regex = [
312+
('creditor_account', G_ALL %70),
313+
]
314+
315+
#specific to withdrawal
316+
# TODO FIXME it's look like there is something wrong in
317+
# confb norme indeed 35+4 != 70 and 35 != 70 :S outch!
318+
#class ParserRUM(Parser):
319+
# _code = 'RUM'
320+
# _regex = [
321+
# ('mandate_identification', G_ALL %35),
322+
# ('sequence_type', G_ALL %4),
323+
# ]
324+
#
325+
#class ParserCPY(Parser):
326+
# _code = 'CPY'
327+
# _regex = [
328+
# ('debtor_account', G_ALL %35),
329+
# ]
330+
#
71331

72332
def parse_amount(amount_str, nb_of_dec):
73333
""" return a numerical amount from the cfonb amount string

0 commit comments

Comments
 (0)