Merge lp:~camptocamp/banking-addons/bank-statement-reconcile-70-improve-import-usability into lp:banking-addons/bank-statement-reconcile-70

Proposed by Nicolas Bessi - Camptocamp
Status: Merged
Merged at revision: 86
Proposed branch: lp:~camptocamp/banking-addons/bank-statement-reconcile-70-improve-import-usability
Merge into: lp:banking-addons/bank-statement-reconcile-70
Diff against target: 216 lines (+77/-33)
3 files modified
account_statement_base_import/parser/file_parser.py (+65/-27)
account_statement_base_import/parser/generic_file_parser.py (+9/-4)
account_statement_transactionid_import/parser/transactionid_file_parser.py (+3/-2)
To merge this branch: bzr merge lp:~camptocamp/banking-addons/bank-statement-reconcile-70-improve-import-usability
Reviewer Review Type Date Requested Status
Alexandre Fayolle - camptocamp code review, no test Approve
Guewen Baconnier @ Camptocamp code review, no test Approve
Nicolas Bessi - Camptocamp Pending
Review via email: mp+151270@code.launchpad.net

This proposal supersedes a proposal from 2013-02-15.

Description of the change

Improve statement import global usability by retuning usable error message while parsing files.

Allows empty value for float in parsed CSV.

Minor code improvements.

To post a comment you must log in.
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : Posted in a previous version of this proposal

Nitpickings

l.30
s/Invalide/invalid/
s/please/Please/
remove extraneous parenthesis around `ftype`

l.40
remove extraneous parenthesis around `col`

l.80,93,113,128
I would recommend the new syntax python3-like for the exceptions as it is more readable
    except Exception as err:
BTW, I don't have the context here, but don't we have a more precise exception to catch?

l.147
s/convertion/conversion/

l.149
    return float(val) if val else 0.0

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : Posted in a previous version of this proposal

l19: do not use a mutable value as a default value. Please replace with an empty tuple.

review: Needs Fixing (code review, no test)
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote : Posted in a previous version of this proposal

Fixes applied.
convertion/conversion typo also fixed in other files.

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) : Posted in a previous version of this proposal
review: Needs Resubmitting
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

LGTM

review: Approve (code review, no test)
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

merged, with a small improvement in __openerp__.py

review: Approve (code review, no test)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account_statement_base_import/parser/file_parser.py'
2--- account_statement_base_import/parser/file_parser.py 2012-12-20 13:37:01 +0000
3+++ account_statement_base_import/parser/file_parser.py 2013-03-01 15:17:23 +0000
4@@ -17,8 +17,8 @@
5 # along with this program. If not, see <http://www.gnu.org/licenses/>.
6 #
7 ##############################################################################
8-
9 from openerp.tools.translate import _
10+from openerp.osv.osv import except_osv
11 import tempfile
12 import datetime
13 from parser import BankStatementImportParser
14@@ -34,12 +34,13 @@
15 Generic abstract class for defining parser for .csv or .xls file format.
16 """
17
18- def __init__(self, parse_name, keys_to_validate=[], ftype='csv', convertion_dict=None, header=None, *args, **kwargs):
19+ def __init__(self, parse_name, keys_to_validate=None, ftype='csv', conversion_dict=None,
20+ header=None, *args, **kwargs):
21 """
22 :param char: parse_name : The name of the parser
23 :param list: keys_to_validate : contain the key that need to be present in the file
24 :param char ftype: extension of the file (could be csv or xls)
25- :param: convertion_dict : keys and type to convert of every column in the file like
26+ :param: conversion_dict : keys and type to convert of every column in the file like
27 {
28 'ref': unicode,
29 'label': unicode,
30@@ -54,9 +55,10 @@
31 if ftype in ('csv', 'xls'):
32 self.ftype = ftype
33 else:
34- raise Exception(_('Invalide file type %s. please use csv or xls') % (ftype))
35- self.keys_to_validate = keys_to_validate
36- self.convertion_dict = convertion_dict
37+ raise except_osv(_('User Error'),
38+ _('Invalid file type %s. Please use csv or xls') % ftype)
39+ self.keys_to_validate = keys_to_validate if keys_to_validate is not None else []
40+ self.conversion_dict = conversion_dict
41 self.fieldnames = header
42 self._datemode = 0 # used only for xls documents,
43 # 0 means Windows mode (1900 based dates).
44@@ -99,7 +101,8 @@
45 parsed_cols = self.result_row_list[0].keys()
46 for col in self.keys_to_validate:
47 if col not in parsed_cols:
48- raise Exception(_('Column %s not present in file') % (col))
49+ raise except_osv(_('Invalid data'),
50+ _('Column %s not present in file') % col)
51 return True
52
53 def _post(self, *args, **kwargs):
54@@ -128,17 +131,13 @@
55 wb_file.write(self.filebuffer)
56 # We ensure that cursor is at beginig of file
57 wb_file.seek(0)
58- wb = xlrd.open_workbook(wb_file.name)
59- self._datemode = wb.datemode
60- sheet = wb.sheet_by_index(0)
61- header = sheet.row_values(0)
62- res = []
63- for rownum in range(1, sheet.nrows):
64- res.append(dict(zip(header, sheet.row_values(rownum))))
65- try:
66- wb_file.close()
67- except Exception, e:
68- pass # file is already closed
69+ with xlrd.open_workbook(wb_file.name) as wb:
70+ self._datemode = wb.datemode
71+ sheet = wb.sheet_by_index(0)
72+ header = sheet.row_values(0)
73+ res = []
74+ for rownum in range(1, sheet.nrows):
75+ res.append(dict(zip(header, sheet.row_values(rownum))))
76 return res
77
78 def _from_csv(self, result_set, conversion_rules):
79@@ -149,11 +148,30 @@
80 for line in result_set:
81 for rule in conversion_rules:
82 if conversion_rules[rule] == datetime.datetime:
83- date_string = line[rule].split(' ')[0]
84- line[rule] = datetime.datetime.strptime(date_string,
85- '%Y-%m-%d')
86+ try:
87+ date_string = line[rule].split(' ')[0]
88+ line[rule] = datetime.datetime.strptime(date_string,
89+ '%Y-%m-%d')
90+ except ValueError as err:
91+ raise except_osv(_("Date format is not valid."),
92+ _(" It should be YYYY-MM-DD for column: %s"
93+ " value: %s \n \n"
94+ " \n Please check the line with ref: %s"
95+ " \n \n Detail: %s") % (rule,
96+ line.get(rule, _('Missing')),
97+ line.get('ref', line),
98+ repr(err)))
99 else:
100- line[rule] = conversion_rules[rule](line[rule])
101+ try:
102+ line[rule] = conversion_rules[rule](line[rule])
103+ except Exception as err:
104+ raise except_osv(_('Invalid data'),
105+ _("Value %s of column %s is not valid."
106+ "\n Please check the line with ref %s:"
107+ "\n \n Detail: %s") % (line.get(rule, _('Missing')),
108+ rule,
109+ line.get('ref', line),
110+ repr(err)))
111 return result_set
112
113 def _from_xls(self, result_set, conversion_rules):
114@@ -164,17 +182,37 @@
115 for line in result_set:
116 for rule in conversion_rules:
117 if conversion_rules[rule] == datetime.datetime:
118- t_tuple = xlrd.xldate_as_tuple(line[rule], self._datemode)
119- line[rule] = datetime.datetime(*t_tuple)
120+ try:
121+ t_tuple = xlrd.xldate_as_tuple(line[rule], self._datemode)
122+ line[rule] = datetime.datetime(*t_tuple)
123+ except Exception as err:
124+ raise except_osv(_("Date format is not valid"),
125+ _("Please modify the cell formatting to date format"
126+ " for column: %s"
127+ " value: %s"
128+ "\n Please check the line with ref: %s"
129+ "\n \n Detail: %s") % (rule,
130+ line.get(rule, _('Missing')),
131+ line.get('ref', line),
132+ repr(err)))
133 else:
134- line[rule] = conversion_rules[rule](line[rule])
135+ try:
136+ line[rule] = conversion_rules[rule](line[rule])
137+ except Exception as err:
138+ raise except_osv(_('Invalid data'),
139+ _("Value %s of column %s is not valid."
140+ "\n Please check the line with ref %s:"
141+ "\n \n Detail: %s") % (line.get(rule, _('Missing')),
142+ rule,
143+ line.get('ref', line),
144+ repr(err)))
145 return result_set
146
147 def _cast_rows(self, *args, **kwargs):
148 """
149- Convert the self.result_row_list using the self.convertion_dict providen.
150+ Convert the self.result_row_list using the self.conversion_dict providen.
151 We call here _from_xls or _from_csv depending on the self.ftype variable.
152 """
153 func = getattr(self, '_from_%s' % self.ftype)
154- res = func(self.result_row_list, self.convertion_dict)
155+ res = func(self.result_row_list, self.conversion_dict)
156 return res
157
158=== modified file 'account_statement_base_import/parser/generic_file_parser.py'
159--- account_statement_base_import/parser/generic_file_parser.py 2012-12-20 13:37:01 +0000
160+++ account_statement_base_import/parser/generic_file_parser.py 2013-03-01 15:17:23 +0000
161@@ -29,6 +29,11 @@
162 except:
163 raise Exception(_('Please install python lib xlrd'))
164
165+def float_or_zero(val):
166+ """ Conversion function used to manage
167+ empty string into float usecase"""
168+ return float(val) if val else 0.0
169+
170
171 class GenericFileParser(FileParser):
172 """
173@@ -38,16 +43,16 @@
174 """
175
176 def __init__(self, parse_name, ftype='csv'):
177- convertion_dict = {
178+ conversion_dict = {
179 'ref': unicode,
180 'label': unicode,
181 'date': datetime.datetime,
182- 'amount': float,
183- 'commission_amount': float
184+ 'amount': float_or_zero,
185+ 'commission_amount': float_or_zero
186 }
187 # Order of cols does not matter but first row of the file has to be header
188 keys_to_validate = ['ref', 'label', 'date', 'amount', 'commission_amount']
189- super(GenericFileParser, self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, convertion_dict=convertion_dict)
190+ super(GenericFileParser, self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, conversion_dict=conversion_dict)
191
192 @classmethod
193 def parser_for(cls, parser_name):
194
195=== modified file 'account_statement_transactionid_import/parser/transactionid_file_parser.py'
196--- account_statement_transactionid_import/parser/transactionid_file_parser.py 2012-12-20 13:37:01 +0000
197+++ account_statement_transactionid_import/parser/transactionid_file_parser.py 2013-03-01 15:17:23 +0000
198@@ -30,7 +30,7 @@
199 """
200
201 def __init__(self, parse_name, ftype='csv'):
202- convertion_dict = {
203+ conversion_dict = {
204 'transaction_id': unicode,
205 'label': unicode,
206 'date': datetime.datetime,
207@@ -39,7 +39,8 @@
208 }
209 # Order of cols does not matter but first row of the file has to be header
210 keys_to_validate = ['transaction_id', 'label', 'date', 'amount', 'commission_amount']
211- super(TransactionIDFileParser, self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, convertion_dict=convertion_dict)
212+ super(TransactionIDFileParser, self).__init__(parse_name, keys_to_validate=keys_to_validate,
213+ ftype=ftype, conversion_dict=conversion_dict)
214
215 @classmethod
216 def parser_for(cls, parser_name):

Subscribers

People subscribed via source and target branches