Merge lp:~akretion-team/openobject-addons/fixes_for_c2c_currency_rate_update into lp:openobject-addons/extra-5.0
- fixes_for_c2c_currency_rate_update
- Merge into stable_5.0-extra-addons
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 4680 | ||||
Proposed branch: | lp:~akretion-team/openobject-addons/fixes_for_c2c_currency_rate_update | ||||
Merge into: | lp:openobject-addons/extra-5.0 | ||||
Diff against target: |
558 lines (+207/-137) 4 files modified
c2c_currency_rate_update/__terp__.py (+18/-21) c2c_currency_rate_update/company.py (+7/-12) c2c_currency_rate_update/currency_rate_update.py (+179/-102) c2c_currency_rate_update/currency_rate_update.xml (+3/-2) |
||||
To merge this branch: | bzr merge lp:~akretion-team/openobject-addons/fixes_for_c2c_currency_rate_update | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+40124@code.launchpad.net |
Commit message
[Fix] bug LP:645263
Description of the change
[FIX] Fixes the 3 issues described on launchpad bug report #645263 i.e. :
. ported from outdated pyXML lib to new lxml lib for XML parsing
I used the etree/xpath API, not the objectify API.
. avoid to go through an exception when there is no company_id
field on res_currency (which is the default setup)
. fix the computation of the rates for the ECB and PL NBP webservices
when using the reference currency of the webservice (respectively 'EUR'
and 'PLN') as company_currency. Fix the computation of the rates for
the Admin.ch webservice when you want to update 'CHF' and
company_
Add a warning in the logs when the date of the currency rate obtained
from the webservice is different than today's date.
Cleaner error messages.
Better module description.
Fix various typos.
Removed prints.
Preview Diff
1 | === modified file 'c2c_currency_rate_update/__terp__.py' |
2 | --- c2c_currency_rate_update/__terp__.py 2009-11-20 14:55:43 +0000 |
3 | +++ c2c_currency_rate_update/__terp__.py 2010-12-27 13:29:09 +0000 |
4 | @@ -8,41 +8,39 @@ |
5 | Import exchange rates from three different sources on the internet : |
6 | |
7 | 1. Admin.ch |
8 | - Updated daily, source in CHF. Source type is XML based. |
9 | - |
10 | - |
11 | -3. European Central Bank (ported by Grzegorz Grzelak) |
12 | + Updated daily, source in CHF. |
13 | + |
14 | +2. European Central Bank (ported by Grzegorz Grzelak) |
15 | The reference rates are based on the regular daily concertation procedure between |
16 | central banks within and outside the European System of Central Banks, |
17 | which normally takes place at 2.15 p.m. (14:15) ECB time. Source in EUR. |
18 | http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html |
19 | |
20 | -4. Google Finance !!! TO be ported |
21 | +3. Yahoo Finance |
22 | + Updated daily |
23 | + |
24 | +4. Polish National Bank (Narodowy Bank Polski) (contribution by Grzegorz Grzelak) |
25 | + Takes official rates from www.nbp.pl. Adds rate table symbol in log. |
26 | + You should check when rates should apply to bookkeeping. If next day you should |
27 | + change the update hour in schedule settings because in OpenERP they apply from |
28 | + date of update (date - no hours). |
29 | + |
30 | +In the roadmap : Google Finance. |
31 | Updated daily from Citibank N.A., source in EUR. Information may be delayed. |
32 | This is parsed from an HTML page, so it may be broken at anytime. |
33 | |
34 | -5. Yahoo Finance |
35 | - Updated daily |
36 | - |
37 | -6. Polish National Bank (Narodowy Bank Polski) (contribution by Grzegorz Grzelak) |
38 | - Takes official rates from www.nbp.pl. Adds rate table symbol in log. Works only |
39 | - when main currency is PLN. |
40 | - You should check when rates should apply to bookkeeping. If next day you should |
41 | - change the update hour in schedule settings because in OpenERP they apply from |
42 | - date of update (date - no hours). |
43 | - |
44 | The update can be set under de company form. |
45 | -You can set for each services wich currency you want to update |
46 | -The log of the update are visible under the service note |
47 | -You can active or deactivate the update |
48 | +You can set for each services which currency you want to update. |
49 | +The log of the update are visible under the service note. |
50 | +You can active or deactivate the update. |
51 | The module uses internal ir_cron feature from OpenERP, so the job is launched once |
52 | the server starts if the 'first execute date' is before the current day. |
53 | -the module support multy company currency in two way : |
54 | +The module supports multi-company currency in two way : |
55 | the currencies are shared, you can set currency update only on one |
56 | company |
57 | the currency are separated, you can set currency on every company |
58 | separately |
59 | -Afunction field let you know your currency configuration |
60 | +A function field let you know your currency configuration. |
61 | |
62 | If in multi-company mode, the base currency will be the first company's currency |
63 | found in database. |
64 | @@ -52,7 +50,6 @@ |
65 | "update_xml" : [ |
66 | "currency_rate_update.xml", |
67 | "company_view.xml", |
68 | - |
69 | ], |
70 | "demo_xml" : [], |
71 | "active": False, |
72 | |
73 | === modified file 'c2c_currency_rate_update/company.py' |
74 | --- c2c_currency_rate_update/company.py 2010-08-19 13:35:39 +0000 |
75 | +++ c2c_currency_rate_update/company.py 2010-12-27 13:29:09 +0000 |
76 | @@ -31,19 +31,15 @@ |
77 | import netsvc |
78 | from osv import fields, osv |
79 | class res_company(osv.osv): |
80 | - """override company to add currency udate""" |
81 | + """override company to add currency update""" |
82 | |
83 | def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}): |
84 | - "check if multiy company currency is enable" |
85 | + "check if multi company currency is enabled" |
86 | result = {} |
87 | - enable = 1 |
88 | - try : |
89 | - #if we are in a mutli company mode the company_id col should exsits |
90 | - cr.execute('select company_id from res_currency') |
91 | - cr.fetchall() |
92 | - except: |
93 | - cr.rollback() |
94 | + if self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', 'company_id'), ('model', '=', 'res.currency')])==[]: |
95 | enable = 0 |
96 | + else: |
97 | + enable = 1 |
98 | for id in ids: |
99 | result[id] = enable |
100 | return result |
101 | @@ -56,14 +52,13 @@ |
102 | try: |
103 | currency_updater_obj.run_currency_update(cr, uid) |
104 | except Exception, e: |
105 | - print str(e) |
106 | return False |
107 | return True |
108 | |
109 | |
110 | def _on_change_auto_currency_up(self, cr, uid, id, value): |
111 | """handle the activation of the currecny update on compagnies. |
112 | - There are two ways of implementing mutli_company currency, |
113 | + There are two ways of implementing multi_company currency, |
114 | the currency is shared or not. The module take care of the two |
115 | ways. If the currency are shared, you will only be able to set |
116 | auto update on one company, this will avoid to have unusefull cron |
117 | @@ -166,4 +161,4 @@ |
118 | ' not set currency is active on two company' |
119 | ), |
120 | } |
121 | -res_company() |
122 | \ No newline at end of file |
123 | +res_company() |
124 | |
125 | === modified file 'c2c_currency_rate_update/currency_rate_update.py' |
126 | --- c2c_currency_rate_update/currency_rate_update.py 2009-11-20 14:55:43 +0000 |
127 | +++ c2c_currency_rate_update/currency_rate_update.py 2010-12-27 13:29:09 +0000 |
128 | @@ -4,7 +4,17 @@ |
129 | # Copyright (c) 2009 Camptocamp SA |
130 | # @author Nicolas Bessi |
131 | # @source JBA and AWST inpiration |
132 | -# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), |
133 | +# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com) |
134 | +# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr) |
135 | +# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib |
136 | +# - rates given by ECB webservice is now correct even when main_cur <> EUR |
137 | +# - rates given by PL_NBP webservice is now correct even when main_cur <> PLN |
138 | +# - if company_currency <> CHF, you can now update CHF via Admin.ch webservice |
139 | +# (same for EUR with ECB webservice and PLN with NBP webservice) |
140 | +# For more details, see Launchpad bug #645263 |
141 | +# - mecanism to check if rates given by the webservice are "fresh" enough to be |
142 | +# written in OpenERP ('max_delta_days' parameter for each currency update service) |
143 | +# |
144 | # WARNING: This program as such is intended to be used by professional |
145 | # programmers who take the whole responsability of assessing all potential |
146 | # consequences resulting from its eventual inadequacies and bugs |
147 | @@ -28,6 +38,10 @@ |
148 | # |
149 | ############################################################################## |
150 | |
151 | +# TODO "nice to have" : restain the list of currencies that can be added for |
152 | +# a webservice to the list of currencies supported by the Webservice |
153 | +# TODO : implement max_delta_days for Yahoo webservice |
154 | + |
155 | from osv import osv, fields |
156 | import time |
157 | from mx import DateTime |
158 | @@ -69,8 +83,12 @@ |
159 | 'linked company', |
160 | ), |
161 | ##note fileds that will be used as a logger |
162 | - 'note':fields.text('update notice') |
163 | + 'note':fields.text('update notice'), |
164 | + 'max_delta_days': fields.integer('Max delta days', required=True, help="If the time delta between the rate date given by the webservice and the current date exeeds this value, then the currency rate is not updated in OpenERP."), |
165 | } |
166 | + _defaults = { |
167 | + 'max_delta_days': lambda *a: 4, |
168 | + } |
169 | _sql_constraints = [ |
170 | ( |
171 | 'curr_service_unique', |
172 | @@ -79,6 +97,17 @@ |
173 | ) |
174 | ] |
175 | |
176 | + def _check_max_delta_days(self, cr, uid, ids): |
177 | + for i in ids: |
178 | + value_to_check = self.read(cr, uid, i, ['max_delta_days'])['max_delta_days'] |
179 | + if value_to_check >= 0: |
180 | + return True |
181 | + else: return False |
182 | + |
183 | + _constraints = [ |
184 | + (_check_max_delta_days, "'Max delta days' must be >= 0", ['max_delta_days']), |
185 | + ] |
186 | + |
187 | Currency_rate_update_service() |
188 | |
189 | class Currency_rate_update(osv.osv): |
190 | @@ -128,7 +157,6 @@ |
191 | netsvc.LOG_INFO, |
192 | 'warning cron not found one will be created' |
193 | ) |
194 | - print 'warning cron not found one will be created' |
195 | pass # ignore if the cron is missing cause we are going to create it in db |
196 | |
197 | #the cron does not exists |
198 | @@ -163,13 +191,15 @@ |
199 | #we fetch the main currency. The main rate should be set at 1.00 |
200 | main_curr = comp.currency_id.code |
201 | for service in comp.services_to_use : |
202 | - note = service.note and service.note or '' |
203 | + print "comp.services_to_use =", comp.services_to_use |
204 | + note = service.note or '' |
205 | try : |
206 | - ## we initalize the class taht will handle the request |
207 | + ## we initalize the class that will handle the request |
208 | ## and return a dict of rate |
209 | getter = factory.register(service.service) |
210 | + print "getter =", getter |
211 | curr_to_fetch = map(lambda x : x.code, service.currency_to_update) |
212 | - res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr) |
213 | + res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr, service.max_delta_days) |
214 | rate_name = time.strftime('%Y-%m-%d') |
215 | for curr in service.currency_to_update : |
216 | if curr.code == main_curr : |
217 | @@ -192,13 +222,13 @@ |
218 | vals, |
219 | ) |
220 | |
221 | - note = note + "\n currency updated at %s "\ |
222 | - %(str(datetime.today())) |
223 | + note = note + "\n%s currency updated. "\ |
224 | + %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S')) |
225 | note = note + (log_info or '') |
226 | service.write({'note':note}) |
227 | except Exception, e: |
228 | - error_msg = note + "\n !!! %s %s !!!"\ |
229 | - %(str(datetime.today()), str(e)) |
230 | + error_msg = note + "\n%s ERROR : %s"\ |
231 | + %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), str(e)) |
232 | self.logger.notifyChannel(self.LOG_NAME, netsvc.LOG_INFO, str(e)) |
233 | service.write({'note':error_msg}) |
234 | |
235 | @@ -294,8 +324,8 @@ |
236 | ##updated currency this arry will contain the final result |
237 | updated_currency = {} |
238 | |
239 | - def get_updated_currency(self, currency_array, main_currency) : |
240 | - """Interface method that will retriev the currency |
241 | + def get_updated_currency(self, currency_array, main_currency, max_delta_days) : |
242 | + """Interface method that will retrieve the currency |
243 | This function has to be reinplemented in child""" |
244 | raise AbstractMethodError |
245 | |
246 | @@ -316,13 +346,25 @@ |
247 | raise osv.except_osv('Error !', self.MOD_NAME+'Unable to import urllib !') |
248 | except IOError: |
249 | raise osv.except_osv('Error !', self.MOD_NAME+'Web Service does not exist !') |
250 | - |
251 | + |
252 | + def check_rate_date(self, rate_date, max_delta_days): |
253 | + """Check date constrains. WARN : rate_date must be of datetime type""" |
254 | + days_delta = (datetime.today() - rate_date).days |
255 | + if days_delta > max_delta_days: |
256 | + raise Exception('The rate date from ECB (%s) is %d days away from today, which is over the limit (%d days). Rate not updated in OpenERP.'%(rate_date, days_delta, max_delta_days)) |
257 | + # We always have a warning when rate_date <> today |
258 | + rate_date_str = datetime.strftime(rate_date, '%Y-%m-%d') |
259 | + if rate_date_str != datetime.strftime(datetime.today(), '%Y-%m-%d'): |
260 | + self.log_info = "WARNING : the rate date from ECB (%s) is not today's date" % rate_date_str |
261 | + netsvc.Logger().notifyChannel("rate_update", netsvc.LOG_WARNING, "the rate date from ECB (%s) is not today's date" % rate_date_str) |
262 | + |
263 | + |
264 | #Yahoo ################################################################################### |
265 | class Yahoo_getter(Curreny_getter_interface) : |
266 | """Implementation of Currency_getter_factory interface |
267 | for Yahoo finance service""" |
268 | |
269 | - def get_updated_currency(self, currency_array, main_currency): |
270 | + def get_updated_currency(self, currency_array, main_currency, max_delta_days): |
271 | """implementation of abstract method of Curreny_getter_interface""" |
272 | self.validate_cur(main_currency) |
273 | url='http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg' |
274 | @@ -338,144 +380,179 @@ |
275 | raise Exception('Could not update the %s'%(curr)) |
276 | |
277 | return self.updated_currency, self.log_info # empty string added by polish changes |
278 | -##Admin CH ############################################################################ |
279 | +##Admin CH ############################################################################ |
280 | class Admin_ch_getter(Curreny_getter_interface) : |
281 | """Implementation of Currency_getter_factory interface |
282 | for Admin.ch service""" |
283 | - |
284 | - def rate_retrieve(self, node) : |
285 | - """ Parse a dom node to retrieve |
286 | + |
287 | + def rate_retrieve(self, dom, ns, curr) : |
288 | + """ Parse a dom node to retrieve- |
289 | currencies data""" |
290 | res = {} |
291 | - if isinstance(node, list) : |
292 | - node = node[0] |
293 | - res['code'] = node.attributes['code'].value.upper() |
294 | - res['currency'] = node.getElementsByTagName('waehrung')[0].childNodes[0].data |
295 | - res['rate_currency'] = float(node.getElementsByTagName('kurs')[0].childNodes[0].data) |
296 | - res['rate_ref'] = float(res['currency'].split(' ')[0]) |
297 | + xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()"%(curr.lower()) |
298 | + xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()"%(curr.lower()) |
299 | + res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0]) |
300 | + res['rate_ref'] = float((dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[0]) |
301 | return res |
302 | |
303 | - def get_updated_currency(self, currency_array, main_currency): |
304 | + def get_updated_currency(self, currency_array, main_currency, max_delta_days): |
305 | """implementation of abstract method of Curreny_getter_interface""" |
306 | url='http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php' |
307 | #we do not want to update the main currency |
308 | if main_currency in currency_array : |
309 | currency_array.remove(main_currency) |
310 | - from xml.dom.minidom import parseString |
311 | - from xml import xpath |
312 | - rawfile = self.get_url(url) |
313 | - dom = parseString(rawfile) |
314 | + # Move to new XML lib cf Launchpad bug #645263 |
315 | + from lxml import etree |
316 | + logger = netsvc.Logger() |
317 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Admin.ch currency rate service : connecting...") |
318 | + rawfile = self.get_url(url) |
319 | + dom = etree.fromstring(rawfile) |
320 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Admin.ch sent a valid XML file") |
321 | + adminch_ns = {'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse'} |
322 | + rate_date = dom.xpath('/def:wechselkurse/def:datum/text()', namespaces=adminch_ns)[0] |
323 | + rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d') |
324 | + self.check_rate_date(rate_date_datetime, max_delta_days) |
325 | #we dynamically update supported currencies |
326 | - self.supported_currency_array = [] |
327 | + self.supported_currency_array = dom.xpath("/def:wechselkurse/def:devise/@code", namespaces=adminch_ns) |
328 | + self.supported_currency_array = [x.upper() for x in self.supported_currency_array] |
329 | self.supported_currency_array.append('CHF') |
330 | - for el in xpath.Evaluate("/wechselkurse/devise/@code", dom): |
331 | - self.supported_currency_array.append(el.value.upper()) |
332 | + |
333 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array)) |
334 | self.validate_cur(main_currency) |
335 | - #The XML give the value in franc for 1 XX if we are in CHF |
336 | - #we want to have the value for 1 xx in chf |
337 | - #if main currency is not CHF we have to apply a computation on it |
338 | if main_currency != 'CHF': |
339 | - main_xpath = "/wechselkurse/devise[@code='%s']"%(main_currency.lower()) |
340 | - node = xpath.Evaluate(main_xpath, dom) |
341 | - tmp_data = self.rate_retrieve(node) |
342 | - main_rate = tmp_data['rate_currency'] / tmp_data['rate_ref'] |
343 | + main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) |
344 | + # 1 MAIN_CURRENCY = main_rate CHF |
345 | + main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref'] |
346 | for curr in currency_array : |
347 | - curr_xpath = "/wechselkurse/devise[@code='%s']"%(curr.lower()) |
348 | - for node in xpath.Evaluate(curr_xpath, dom): |
349 | - tmp_data = self.rate_retrieve(node) |
350 | - #Source is in CHF, so we transform it into reference currencies |
351 | + self.validate_cur(curr) |
352 | + if curr == 'CHF': |
353 | + rate = main_rate |
354 | + else: |
355 | + curr_data = self.rate_retrieve(dom, adminch_ns, curr) |
356 | + # 1 MAIN_CURRENCY = rate CURR |
357 | if main_currency == 'CHF' : |
358 | - rate = 1 / (tmp_data['rate_currency'] / tmp_data['rate_ref']) |
359 | + rate = curr_data['rate_ref'] / curr_data['rate_currency'] |
360 | else : |
361 | - rate = main_rate / (tmp_data['rate_currency'] / tmp_data['rate_ref']) |
362 | - |
363 | - self.updated_currency[curr] = rate |
364 | - return self.updated_currency, self.log_info # empty string added by polish changes |
365 | - |
366 | -## ECB getter ############################################################################ |
367 | + rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency'] |
368 | + self.updated_currency[curr] = rate |
369 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr) |
370 | + return self.updated_currency, self.log_info |
371 | + |
372 | +## ECB getter ############################################################################ |
373 | + |
374 | class ECB_getter(Curreny_getter_interface) : |
375 | """Implementation of Currency_getter_factory interface |
376 | for ECB service""" |
377 | - |
378 | - def rate_retrieve(self, node) : |
379 | - """ Parse a dom node to retrieve |
380 | + |
381 | + def rate_retrieve(self, dom, ns, curr) : |
382 | + """ Parse a dom node to retrieve- |
383 | currencies data""" |
384 | res = {} |
385 | - if isinstance(node, list) : |
386 | - node = node[0] |
387 | - res['code'] = node.attributes['currency'].value.upper() |
388 | - res['rate_currency'] = float(node.attributes['rate'].value) |
389 | + xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate"%(curr.upper()) |
390 | + res['rate_currency'] = float(dom.xpath(xpath_curr_rate, namespaces=ns)[0]) |
391 | return res |
392 | |
393 | - def get_updated_currency(self, currency_array, main_currency): |
394 | + def get_updated_currency(self, currency_array, main_currency, max_delta_days): |
395 | """implementation of abstract method of Curreny_getter_interface""" |
396 | url='http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' |
397 | + # Important : as explained on the ECB web site, the currencies are |
398 | + # at the beginning of the afternoon ; so, until 3 p.m. Paris time |
399 | + # the currency rates are the ones of trading day N-1 |
400 | + # see http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html |
401 | + |
402 | #we do not want to update the main currency |
403 | if main_currency in currency_array : |
404 | currency_array.remove(main_currency) |
405 | - from xml.dom.minidom import parseString |
406 | - from xml import xpath |
407 | - rawfile = self.get_url(url) |
408 | - dom = parseString(rawfile) |
409 | + # Move to new XML lib cf Launchpad bug #645263 |
410 | + from lxml import etree |
411 | + logger = netsvc.Logger() |
412 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "ECB currency rate service : connecting...") |
413 | + rawfile = self.get_url(url) |
414 | + dom = etree.fromstring(rawfile) |
415 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "ECB sent a valid XML file") |
416 | + ecb_ns = {'gesmes': 'http://www.gesmes.org/xml/2002-08-01', 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'} |
417 | + rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', namespaces=ecb_ns)[0] |
418 | + rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d') |
419 | + self.check_rate_date(rate_date_datetime, max_delta_days) |
420 | #we dynamically update supported currencies |
421 | - self.supported_currency_array = [] |
422 | + self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", namespaces=ecb_ns) |
423 | self.supported_currency_array.append('EUR') |
424 | - for el in xpath.Evaluate("//Cube/Cube/Cube", dom): |
425 | - self.supported_currency_array.append(el) |
426 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array)) |
427 | self.validate_cur(main_currency) |
428 | - for curr in currency_array : |
429 | - curr_xpath = "//Cube/Cube/Cube[@currency='%s']"%(curr.upper()) |
430 | - for node in xpath.Evaluate(curr_xpath, dom): |
431 | - tmp_data = self.rate_retrieve(node) |
432 | - self.updated_currency[curr] = tmp_data['rate_currency'] |
433 | - return self.updated_currency, self.log_info # empty string added by polish changes |
434 | + if main_currency != 'EUR': |
435 | + main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency) |
436 | + for curr in currency_array: |
437 | + self.validate_cur(curr) |
438 | + if curr == 'EUR': |
439 | + rate = 1 / main_curr_data['rate_currency'] |
440 | + else: |
441 | + curr_data = self.rate_retrieve(dom, ecb_ns, curr) |
442 | + if main_currency == 'EUR': |
443 | + rate = curr_data['rate_currency'] |
444 | + else: |
445 | + rate = curr_data['rate_currency'] / main_curr_data['rate_currency'] |
446 | + self.updated_currency[curr] = rate |
447 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr) |
448 | + return self.updated_currency, self.log_info |
449 | |
450 | -##PL NBP ############################################################################ |
451 | +##PL NBP ############################################################################ |
452 | class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter |
453 | """Implementation of Currency_getter_factory interface |
454 | for PL NBP service""" |
455 | - |
456 | - def rate_retrieve(self, node) : |
457 | - """ Parse a dom node to retrieve |
458 | + |
459 | + def rate_retrieve(self, dom, ns, curr) : |
460 | + """ Parse a dom node to retrieve |
461 | currencies data""" |
462 | res = {} |
463 | - if isinstance(node, list) : |
464 | - node = node[0] |
465 | - res['code'] = node.getElementsByTagName('kod_waluty')[0].childNodes[0].data # pl changes |
466 | -# res['currency'] = node.getElementsByTagName('waehrung')[0].childNodes[0].data Removed in Polish changes |
467 | -# res['currency'] = res['code'] #pl changes |
468 | - res['rate_currency'] = float(node.getElementsByTagName('kurs_sredni')[0].childNodes[0].data.replace(',','.')) #pl changes |
469 | - res['rate_ref'] = float(node.getElementsByTagName('przelicznik')[0].childNodes[0].data) #pl changes |
470 | - |
471 | + xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()"%(curr.upper()) |
472 | + xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()"%(curr.upper()) |
473 | + res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',','.')) |
474 | + res['rate_ref'] = float(dom.xpath(xpath_rate_ref, namespaces=ns)[0]) |
475 | return res |
476 | |
477 | - def get_updated_currency(self, currency_array, main_currency): |
478 | + def get_updated_currency(self, currency_array, main_currency, max_delta_days): |
479 | """implementation of abstract method of Curreny_getter_interface""" |
480 | url='http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one |
481 | #we do not want to update the main currency |
482 | if main_currency in currency_array : |
483 | currency_array.remove(main_currency) |
484 | - from xml.dom.minidom import parseString |
485 | - from xml import xpath |
486 | - rawfile = self.get_url(url) |
487 | - dom = parseString(rawfile) |
488 | - node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name |
489 | - if isinstance(node, list) : |
490 | - node = node[0] |
491 | - self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data |
492 | - self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name |
493 | + # Move to new XML lib cf Launchpad bug #645263 |
494 | + from lxml import etree |
495 | + logger = netsvc.Logger() |
496 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "NBP.pl currency rate service : connecting...") |
497 | + rawfile = self.get_url(url) |
498 | + dom = etree.fromstring(rawfile) # If rawfile is not XML, it crashes here |
499 | + ns = {} # Cool, there are no namespaces ! |
500 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "NBP.pl sent a valid XML file") |
501 | + #node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name |
502 | + #if isinstance(node, list) : |
503 | + # node = node[0] |
504 | + #self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data |
505 | + #self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name |
506 | |
507 | + rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', namespaces=ns)[0] |
508 | + rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d') |
509 | + self.check_rate_date(rate_date_datetime, max_delta_days) |
510 | #we dynamically update supported currencies |
511 | - self.supported_currency_array = [] |
512 | + self.supported_currency_array = dom.xpath('/tabela_kursow/pozycja/kod_waluty/text()', namespaces=ns) |
513 | self.supported_currency_array.append('PLN') |
514 | - for el in xpath.Evaluate("/tabela_kursow/pozycja/kod_waluty/text()", dom): |
515 | - self.supported_currency_array.append(el) |
516 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array)) |
517 | self.validate_cur(main_currency) |
518 | + if main_currency != 'PLN': |
519 | + main_curr_data = self.rate_retrieve(dom, ns, main_currency) |
520 | + # 1 MAIN_CURRENCY = main_rate PLN |
521 | + main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref'] |
522 | for curr in currency_array : |
523 | - curr_xpath = "/tabela_kursow/pozycja[kod_waluty='%s']"%(curr.upper()) |
524 | - for node in xpath.Evaluate(curr_xpath, dom): |
525 | - tmp_data = self.rate_retrieve(node) |
526 | - #Source is in PLN, so we transform it into reference currencies |
527 | - rate = 1 / (tmp_data['rate_currency'] / tmp_data['rate_ref']) |
528 | - self.updated_currency[curr] = rate |
529 | + self.validate_cur(curr) |
530 | + if curr == 'PLN': |
531 | + rate = main_rate |
532 | + else: |
533 | + curr_data = self.rate_retrieve(dom, ns, curr) |
534 | + # 1 MAIN_CURRENCY = rate CURR |
535 | + if main_currency == 'PLN': |
536 | + rate = curr_data['rate_ref'] / curr_data['rate_currency'] |
537 | + else: |
538 | + rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency'] |
539 | + self.updated_currency[curr] = rate |
540 | + logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr) |
541 | return self.updated_currency, self.log_info |
542 | |
543 | === modified file 'c2c_currency_rate_update/currency_rate_update.xml' |
544 | --- c2c_currency_rate_update/currency_rate_update.xml 2009-07-10 13:34:11 +0000 |
545 | +++ c2c_currency_rate_update/currency_rate_update.xml 2010-12-27 13:29:09 +0000 |
546 | @@ -23,9 +23,10 @@ |
547 | <form string="Rate"> |
548 | <field name="service"/> |
549 | <field name="company_id"/> |
550 | - <separator string="Currencies to update with this services" colspan="4"/> |
551 | + <field name="max_delta_days"/> |
552 | + <separator string="Currencies to update with this service" colspan="4"/> |
553 | <field name="currency_to_update" colspan="4" nolabel="1"/> |
554 | - <separator string="note" colspan="4"/> |
555 | + <separator string="Logs" colspan="4"/> |
556 | <field name="note" colspan="4" nolabel="1"/> |
557 | </form> |
558 | </field> |