Merge lp:~akretion-team/openobject-addons/fixes_for_c2c_currency_rate_update into lp:openobject-addons/extra-5.0

Proposed by Alexis de Lattre
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
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_currency != 'CHF'

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.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'c2c_currency_rate_update/__terp__.py'
--- c2c_currency_rate_update/__terp__.py 2009-11-20 14:55:43 +0000
+++ c2c_currency_rate_update/__terp__.py 2010-12-27 13:29:09 +0000
@@ -8,41 +8,39 @@
8Import exchange rates from three different sources on the internet :8Import exchange rates from three different sources on the internet :
99
101. Admin.ch101. Admin.ch
11 Updated daily, source in CHF. Source type is XML based.11 Updated daily, source in CHF.
1212
13132. European Central Bank (ported by Grzegorz Grzelak)
143. European Central Bank (ported by Grzegorz Grzelak)
15 The reference rates are based on the regular daily concertation procedure between14 The reference rates are based on the regular daily concertation procedure between
16 central banks within and outside the European System of Central Banks,15 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.16 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.html17 http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html
1918
204. Google Finance !!! TO be ported193. Yahoo Finance
20 Updated daily
21
224. Polish National Bank (Narodowy Bank Polski) (contribution by Grzegorz Grzelak)
23 Takes official rates from www.nbp.pl. Adds rate table symbol in log.
24 You should check when rates should apply to bookkeeping. If next day you should
25 change the update hour in schedule settings because in OpenERP they apply from
26 date of update (date - no hours).
27
28In the roadmap : Google Finance.
21 Updated daily from Citibank N.A., source in EUR. Information may be delayed.29 Updated daily from Citibank N.A., source in EUR. Information may be delayed.
22 This is parsed from an HTML page, so it may be broken at anytime.30 This is parsed from an HTML page, so it may be broken at anytime.
2331
245. Yahoo Finance
25 Updated daily
26
276. Polish National Bank (Narodowy Bank Polski) (contribution by Grzegorz Grzelak)
28 Takes official rates from www.nbp.pl. Adds rate table symbol in log. Works only
29 when main currency is PLN.
30 You should check when rates should apply to bookkeeping. If next day you should
31 change the update hour in schedule settings because in OpenERP they apply from
32 date of update (date - no hours).
33
34The update can be set under de company form. 32The update can be set under de company form.
35You can set for each services wich currency you want to update33You can set for each services which currency you want to update.
36The log of the update are visible under the service note34The log of the update are visible under the service note.
37You can active or deactivate the update35You can active or deactivate the update.
38The module uses internal ir_cron feature from OpenERP, so the job is launched once36The module uses internal ir_cron feature from OpenERP, so the job is launched once
39the server starts if the 'first execute date' is before the current day.37the server starts if the 'first execute date' is before the current day.
40the module support multy company currency in two way :38The module supports multi-company currency in two way :
41 the currencies are shared, you can set currency update only on one 39 the currencies are shared, you can set currency update only on one
42 company40 company
43 the currency are separated, you can set currency on every company41 the currency are separated, you can set currency on every company
44 separately42 separately
45Afunction field let you know your currency configuration 43A function field let you know your currency configuration.
4644
47If in multi-company mode, the base currency will be the first company's currency45If in multi-company mode, the base currency will be the first company's currency
48found in database.46found in database.
@@ -52,7 +50,6 @@
52 "update_xml" : [50 "update_xml" : [
53 "currency_rate_update.xml",51 "currency_rate_update.xml",
54 "company_view.xml",52 "company_view.xml",
55
56 ],53 ],
57 "demo_xml" : [],54 "demo_xml" : [],
58 "active": False,55 "active": False,
5956
=== modified file 'c2c_currency_rate_update/company.py'
--- c2c_currency_rate_update/company.py 2010-08-19 13:35:39 +0000
+++ c2c_currency_rate_update/company.py 2010-12-27 13:29:09 +0000
@@ -31,19 +31,15 @@
31import netsvc31import netsvc
32from osv import fields, osv32from osv import fields, osv
33class res_company(osv.osv):33class res_company(osv.osv):
34 """override company to add currency udate"""34 """override company to add currency update"""
35 35
36 def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}):36 def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}):
37 "check if multiy company currency is enable"37 "check if multi company currency is enabled"
38 result = {}38 result = {}
39 enable = 139 if self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', 'company_id'), ('model', '=', 'res.currency')])==[]:
40 try :
41 #if we are in a mutli company mode the company_id col should exsits
42 cr.execute('select company_id from res_currency')
43 cr.fetchall()
44 except:
45 cr.rollback()
46 enable = 040 enable = 0
41 else:
42 enable = 1
47 for id in ids:43 for id in ids:
48 result[id] = enable44 result[id] = enable
49 return result45 return result
@@ -56,14 +52,13 @@
56 try:52 try:
57 currency_updater_obj.run_currency_update(cr, uid)53 currency_updater_obj.run_currency_update(cr, uid)
58 except Exception, e:54 except Exception, e:
59 print str(e)
60 return False55 return False
61 return True56 return True
62 57
63 58
64 def _on_change_auto_currency_up(self, cr, uid, id, value):59 def _on_change_auto_currency_up(self, cr, uid, id, value):
65 """handle the activation of the currecny update on compagnies.60 """handle the activation of the currecny update on compagnies.
66 There are two ways of implementing mutli_company currency, 61 There are two ways of implementing multi_company currency,
67 the currency is shared or not. The module take care of the two62 the currency is shared or not. The module take care of the two
68 ways. If the currency are shared, you will only be able to set 63 ways. If the currency are shared, you will only be able to set
69 auto update on one company, this will avoid to have unusefull cron 64 auto update on one company, this will avoid to have unusefull cron
@@ -166,4 +161,4 @@
166 ' not set currency is active on two company'161 ' not set currency is active on two company'
167 ),162 ),
168 } 163 }
169res_company()
170\ No newline at end of file164\ No newline at end of file
165res_company()
171166
=== modified file 'c2c_currency_rate_update/currency_rate_update.py'
--- c2c_currency_rate_update/currency_rate_update.py 2009-11-20 14:55:43 +0000
+++ c2c_currency_rate_update/currency_rate_update.py 2010-12-27 13:29:09 +0000
@@ -4,7 +4,17 @@
4# Copyright (c) 2009 Camptocamp SA4# Copyright (c) 2009 Camptocamp SA
5# @author Nicolas Bessi 5# @author Nicolas Bessi
6# @source JBA and AWST inpiration 6# @source JBA and AWST inpiration
7# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com),7# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com)
8# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr)
9# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib
10# - rates given by ECB webservice is now correct even when main_cur <> EUR
11# - rates given by PL_NBP webservice is now correct even when main_cur <> PLN
12# - if company_currency <> CHF, you can now update CHF via Admin.ch webservice
13# (same for EUR with ECB webservice and PLN with NBP webservice)
14# For more details, see Launchpad bug #645263
15# - mecanism to check if rates given by the webservice are "fresh" enough to be
16# written in OpenERP ('max_delta_days' parameter for each currency update service)
17#
8# WARNING: This program as such is intended to be used by professional18# WARNING: This program as such is intended to be used by professional
9# programmers who take the whole responsability of assessing all potential19# programmers who take the whole responsability of assessing all potential
10# consequences resulting from its eventual inadequacies and bugs20# consequences resulting from its eventual inadequacies and bugs
@@ -28,6 +38,10 @@
28#38#
29##############################################################################39##############################################################################
3040
41# TODO "nice to have" : restain the list of currencies that can be added for
42# a webservice to the list of currencies supported by the Webservice
43# TODO : implement max_delta_days for Yahoo webservice
44
31from osv import osv, fields45from osv import osv, fields
32import time46import time
33from mx import DateTime47from mx import DateTime
@@ -69,8 +83,12 @@
69 'linked company',83 'linked company',
70 ),84 ),
71 ##note fileds that will be used as a logger85 ##note fileds that will be used as a logger
72 'note':fields.text('update notice')86 'note':fields.text('update notice'),
87 '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."),
73 }88 }
89 _defaults = {
90 'max_delta_days': lambda *a: 4,
91 }
74 _sql_constraints = [92 _sql_constraints = [
75 (93 (
76 'curr_service_unique', 94 'curr_service_unique',
@@ -79,6 +97,17 @@
79 )97 )
80 ]98 ]
8199
100 def _check_max_delta_days(self, cr, uid, ids):
101 for i in ids:
102 value_to_check = self.read(cr, uid, i, ['max_delta_days'])['max_delta_days']
103 if value_to_check >= 0:
104 return True
105 else: return False
106
107 _constraints = [
108 (_check_max_delta_days, "'Max delta days' must be >= 0", ['max_delta_days']),
109 ]
110
82Currency_rate_update_service()111Currency_rate_update_service()
83112
84class Currency_rate_update(osv.osv):113class Currency_rate_update(osv.osv):
@@ -128,7 +157,6 @@
128 netsvc.LOG_INFO, 157 netsvc.LOG_INFO,
129 'warning cron not found one will be created'158 'warning cron not found one will be created'
130 )159 )
131 print 'warning cron not found one will be created'
132 pass # ignore if the cron is missing cause we are going to create it in db160 pass # ignore if the cron is missing cause we are going to create it in db
133 161
134 #the cron does not exists162 #the cron does not exists
@@ -163,13 +191,15 @@
163 #we fetch the main currency. The main rate should be set at 1.00191 #we fetch the main currency. The main rate should be set at 1.00
164 main_curr = comp.currency_id.code192 main_curr = comp.currency_id.code
165 for service in comp.services_to_use :193 for service in comp.services_to_use :
166 note = service.note and service.note or ''194 print "comp.services_to_use =", comp.services_to_use
195 note = service.note or ''
167 try :196 try :
168 ## we initalize the class taht will handle the request197 ## we initalize the class that will handle the request
169 ## and return a dict of rate198 ## and return a dict of rate
170 getter = factory.register(service.service)199 getter = factory.register(service.service)
200 print "getter =", getter
171 curr_to_fetch = map(lambda x : x.code, service.currency_to_update)201 curr_to_fetch = map(lambda x : x.code, service.currency_to_update)
172 res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr)202 res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr, service.max_delta_days)
173 rate_name = time.strftime('%Y-%m-%d')203 rate_name = time.strftime('%Y-%m-%d')
174 for curr in service.currency_to_update :204 for curr in service.currency_to_update :
175 if curr.code == main_curr :205 if curr.code == main_curr :
@@ -192,13 +222,13 @@
192 vals,222 vals,
193 )223 )
194 224
195 note = note + "\n currency updated at %s "\225 note = note + "\n%s currency updated. "\
196 %(str(datetime.today()))226 %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'))
197 note = note + (log_info or '')227 note = note + (log_info or '')
198 service.write({'note':note})228 service.write({'note':note})
199 except Exception, e:229 except Exception, e:
200 error_msg = note + "\n !!! %s %s !!!"\230 error_msg = note + "\n%s ERROR : %s"\
201 %(str(datetime.today()), str(e))231 %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), str(e))
202 self.logger.notifyChannel(self.LOG_NAME, netsvc.LOG_INFO, str(e))232 self.logger.notifyChannel(self.LOG_NAME, netsvc.LOG_INFO, str(e))
203 service.write({'note':error_msg})233 service.write({'note':error_msg})
204 234
@@ -294,8 +324,8 @@
294 ##updated currency this arry will contain the final result324 ##updated currency this arry will contain the final result
295 updated_currency = {}325 updated_currency = {}
296 326
297 def get_updated_currency(self, currency_array, main_currency) :327 def get_updated_currency(self, currency_array, main_currency, max_delta_days) :
298 """Interface method that will retriev the currency328 """Interface method that will retrieve the currency
299 This function has to be reinplemented in child"""329 This function has to be reinplemented in child"""
300 raise AbstractMethodError330 raise AbstractMethodError
301 331
@@ -316,13 +346,25 @@
316 raise osv.except_osv('Error !', self.MOD_NAME+'Unable to import urllib !')346 raise osv.except_osv('Error !', self.MOD_NAME+'Unable to import urllib !')
317 except IOError:347 except IOError:
318 raise osv.except_osv('Error !', self.MOD_NAME+'Web Service does not exist !')348 raise osv.except_osv('Error !', self.MOD_NAME+'Web Service does not exist !')
319 349
350 def check_rate_date(self, rate_date, max_delta_days):
351 """Check date constrains. WARN : rate_date must be of datetime type"""
352 days_delta = (datetime.today() - rate_date).days
353 if days_delta > max_delta_days:
354 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))
355 # We always have a warning when rate_date <> today
356 rate_date_str = datetime.strftime(rate_date, '%Y-%m-%d')
357 if rate_date_str != datetime.strftime(datetime.today(), '%Y-%m-%d'):
358 self.log_info = "WARNING : the rate date from ECB (%s) is not today's date" % rate_date_str
359 netsvc.Logger().notifyChannel("rate_update", netsvc.LOG_WARNING, "the rate date from ECB (%s) is not today's date" % rate_date_str)
360
361
320#Yahoo ################################################################################### 362#Yahoo ###################################################################################
321class Yahoo_getter(Curreny_getter_interface) :363class Yahoo_getter(Curreny_getter_interface) :
322 """Implementation of Currency_getter_factory interface364 """Implementation of Currency_getter_factory interface
323 for Yahoo finance service"""365 for Yahoo finance service"""
324 366
325 def get_updated_currency(self, currency_array, main_currency):367 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
326 """implementation of abstract method of Curreny_getter_interface"""368 """implementation of abstract method of Curreny_getter_interface"""
327 self.validate_cur(main_currency)369 self.validate_cur(main_currency)
328 url='http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg'370 url='http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg'
@@ -338,144 +380,179 @@
338 raise Exception('Could not update the %s'%(curr))380 raise Exception('Could not update the %s'%(curr))
339 381
340 return self.updated_currency, self.log_info # empty string added by polish changes382 return self.updated_currency, self.log_info # empty string added by polish changes
341##Admin CH ############################################################################ 383##Admin CH ############################################################################
342class Admin_ch_getter(Curreny_getter_interface) :384class Admin_ch_getter(Curreny_getter_interface) :
343 """Implementation of Currency_getter_factory interface385 """Implementation of Currency_getter_factory interface
344 for Admin.ch service"""386 for Admin.ch service"""
345 387
346 def rate_retrieve(self, node) :388 def rate_retrieve(self, dom, ns, curr) :
347 """ Parse a dom node to retrieve 389 """ Parse a dom node to retrieve-
348 currencies data"""390 currencies data"""
349 res = {}391 res = {}
350 if isinstance(node, list) :392 xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()"%(curr.lower())
351 node = node[0]393 xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()"%(curr.lower())
352 res['code'] = node.attributes['code'].value.upper()394 res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0])
353 res['currency'] = node.getElementsByTagName('waehrung')[0].childNodes[0].data395 res['rate_ref'] = float((dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[0])
354 res['rate_currency'] = float(node.getElementsByTagName('kurs')[0].childNodes[0].data)
355 res['rate_ref'] = float(res['currency'].split(' ')[0])
356 return res396 return res
357397
358 def get_updated_currency(self, currency_array, main_currency):398 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
359 """implementation of abstract method of Curreny_getter_interface"""399 """implementation of abstract method of Curreny_getter_interface"""
360 url='http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php'400 url='http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php'
361 #we do not want to update the main currency401 #we do not want to update the main currency
362 if main_currency in currency_array :402 if main_currency in currency_array :
363 currency_array.remove(main_currency)403 currency_array.remove(main_currency)
364 from xml.dom.minidom import parseString404 # Move to new XML lib cf Launchpad bug #645263
365 from xml import xpath405 from lxml import etree
366 rawfile = self.get_url(url) 406 logger = netsvc.Logger()
367 dom = parseString(rawfile)407 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Admin.ch currency rate service : connecting...")
408 rawfile = self.get_url(url)
409 dom = etree.fromstring(rawfile)
410 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Admin.ch sent a valid XML file")
411 adminch_ns = {'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse'}
412 rate_date = dom.xpath('/def:wechselkurse/def:datum/text()', namespaces=adminch_ns)[0]
413 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
414 self.check_rate_date(rate_date_datetime, max_delta_days)
368 #we dynamically update supported currencies415 #we dynamically update supported currencies
369 self.supported_currency_array = []416 self.supported_currency_array = dom.xpath("/def:wechselkurse/def:devise/@code", namespaces=adminch_ns)
417 self.supported_currency_array = [x.upper() for x in self.supported_currency_array]
370 self.supported_currency_array.append('CHF')418 self.supported_currency_array.append('CHF')
371 for el in xpath.Evaluate("/wechselkurse/devise/@code", dom):419
372 self.supported_currency_array.append(el.value.upper())420 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array))
373 self.validate_cur(main_currency)421 self.validate_cur(main_currency)
374 #The XML give the value in franc for 1 XX if we are in CHF
375 #we want to have the value for 1 xx in chf
376 #if main currency is not CHF we have to apply a computation on it
377 if main_currency != 'CHF':422 if main_currency != 'CHF':
378 main_xpath = "/wechselkurse/devise[@code='%s']"%(main_currency.lower())423 main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency)
379 node = xpath.Evaluate(main_xpath, dom)424 # 1 MAIN_CURRENCY = main_rate CHF
380 tmp_data = self.rate_retrieve(node)425 main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref']
381 main_rate = tmp_data['rate_currency'] / tmp_data['rate_ref']
382 for curr in currency_array :426 for curr in currency_array :
383 curr_xpath = "/wechselkurse/devise[@code='%s']"%(curr.lower())427 self.validate_cur(curr)
384 for node in xpath.Evaluate(curr_xpath, dom): 428 if curr == 'CHF':
385 tmp_data = self.rate_retrieve(node)429 rate = main_rate
386 #Source is in CHF, so we transform it into reference currencies430 else:
431 curr_data = self.rate_retrieve(dom, adminch_ns, curr)
432 # 1 MAIN_CURRENCY = rate CURR
387 if main_currency == 'CHF' :433 if main_currency == 'CHF' :
388 rate = 1 / (tmp_data['rate_currency'] / tmp_data['rate_ref'])434 rate = curr_data['rate_ref'] / curr_data['rate_currency']
389 else :435 else :
390 rate = main_rate / (tmp_data['rate_currency'] / tmp_data['rate_ref'])436 rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']
391437 self.updated_currency[curr] = rate
392 self.updated_currency[curr] = rate438 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
393 return self.updated_currency, self.log_info # empty string added by polish changes439 return self.updated_currency, self.log_info
394440
395## ECB getter ############################################################################ 441## ECB getter ############################################################################
442
396class ECB_getter(Curreny_getter_interface) :443class ECB_getter(Curreny_getter_interface) :
397 """Implementation of Currency_getter_factory interface444 """Implementation of Currency_getter_factory interface
398 for ECB service"""445 for ECB service"""
399 446
400 def rate_retrieve(self, node) :447 def rate_retrieve(self, dom, ns, curr) :
401 """ Parse a dom node to retrieve 448 """ Parse a dom node to retrieve-
402 currencies data"""449 currencies data"""
403 res = {}450 res = {}
404 if isinstance(node, list) :451 xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate"%(curr.upper())
405 node = node[0]452 res['rate_currency'] = float(dom.xpath(xpath_curr_rate, namespaces=ns)[0])
406 res['code'] = node.attributes['currency'].value.upper()
407 res['rate_currency'] = float(node.attributes['rate'].value)
408 return res453 return res
409454
410 def get_updated_currency(self, currency_array, main_currency):455 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
411 """implementation of abstract method of Curreny_getter_interface"""456 """implementation of abstract method of Curreny_getter_interface"""
412 url='http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'457 url='http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
458 # Important : as explained on the ECB web site, the currencies are
459 # at the beginning of the afternoon ; so, until 3 p.m. Paris time
460 # the currency rates are the ones of trading day N-1
461 # see http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html
462
413 #we do not want to update the main currency463 #we do not want to update the main currency
414 if main_currency in currency_array :464 if main_currency in currency_array :
415 currency_array.remove(main_currency)465 currency_array.remove(main_currency)
416 from xml.dom.minidom import parseString466 # Move to new XML lib cf Launchpad bug #645263
417 from xml import xpath467 from lxml import etree
418 rawfile = self.get_url(url) 468 logger = netsvc.Logger()
419 dom = parseString(rawfile)469 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "ECB currency rate service : connecting...")
470 rawfile = self.get_url(url)
471 dom = etree.fromstring(rawfile)
472 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "ECB sent a valid XML file")
473 ecb_ns = {'gesmes': 'http://www.gesmes.org/xml/2002-08-01', 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'}
474 rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', namespaces=ecb_ns)[0]
475 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
476 self.check_rate_date(rate_date_datetime, max_delta_days)
420 #we dynamically update supported currencies477 #we dynamically update supported currencies
421 self.supported_currency_array = []478 self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", namespaces=ecb_ns)
422 self.supported_currency_array.append('EUR')479 self.supported_currency_array.append('EUR')
423 for el in xpath.Evaluate("//Cube/Cube/Cube", dom):480 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array))
424 self.supported_currency_array.append(el)
425 self.validate_cur(main_currency)481 self.validate_cur(main_currency)
426 for curr in currency_array :482 if main_currency != 'EUR':
427 curr_xpath = "//Cube/Cube/Cube[@currency='%s']"%(curr.upper())483 main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency)
428 for node in xpath.Evaluate(curr_xpath, dom): 484 for curr in currency_array:
429 tmp_data = self.rate_retrieve(node)485 self.validate_cur(curr)
430 self.updated_currency[curr] = tmp_data['rate_currency']486 if curr == 'EUR':
431 return self.updated_currency, self.log_info # empty string added by polish changes487 rate = 1 / main_curr_data['rate_currency']
488 else:
489 curr_data = self.rate_retrieve(dom, ecb_ns, curr)
490 if main_currency == 'EUR':
491 rate = curr_data['rate_currency']
492 else:
493 rate = curr_data['rate_currency'] / main_curr_data['rate_currency']
494 self.updated_currency[curr] = rate
495 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
496 return self.updated_currency, self.log_info
432497
433##PL NBP ############################################################################ 498##PL NBP ############################################################################
434class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter499class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter
435 """Implementation of Currency_getter_factory interface500 """Implementation of Currency_getter_factory interface
436 for PL NBP service"""501 for PL NBP service"""
437 502
438 def rate_retrieve(self, node) :503 def rate_retrieve(self, dom, ns, curr) :
439 """ Parse a dom node to retrieve 504 """ Parse a dom node to retrieve
440 currencies data"""505 currencies data"""
441 res = {}506 res = {}
442 if isinstance(node, list) :507 xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()"%(curr.upper())
443 node = node[0]508 xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()"%(curr.upper())
444 res['code'] = node.getElementsByTagName('kod_waluty')[0].childNodes[0].data # pl changes509 res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',','.'))
445# res['currency'] = node.getElementsByTagName('waehrung')[0].childNodes[0].data Removed in Polish changes510 res['rate_ref'] = float(dom.xpath(xpath_rate_ref, namespaces=ns)[0])
446# res['currency'] = res['code'] #pl changes
447 res['rate_currency'] = float(node.getElementsByTagName('kurs_sredni')[0].childNodes[0].data.replace(',','.')) #pl changes
448 res['rate_ref'] = float(node.getElementsByTagName('przelicznik')[0].childNodes[0].data) #pl changes
449
450 return res511 return res
451512
452 def get_updated_currency(self, currency_array, main_currency):513 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
453 """implementation of abstract method of Curreny_getter_interface"""514 """implementation of abstract method of Curreny_getter_interface"""
454 url='http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one515 url='http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one
455 #we do not want to update the main currency516 #we do not want to update the main currency
456 if main_currency in currency_array :517 if main_currency in currency_array :
457 currency_array.remove(main_currency)518 currency_array.remove(main_currency)
458 from xml.dom.minidom import parseString519 # Move to new XML lib cf Launchpad bug #645263
459 from xml import xpath520 from lxml import etree
460 rawfile = self.get_url(url) 521 logger = netsvc.Logger()
461 dom = parseString(rawfile)522 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "NBP.pl currency rate service : connecting...")
462 node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name523 rawfile = self.get_url(url)
463 if isinstance(node, list) :524 dom = etree.fromstring(rawfile) # If rawfile is not XML, it crashes here
464 node = node[0]525 ns = {} # Cool, there are no namespaces !
465 self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data 526 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "NBP.pl sent a valid XML file")
466 self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name527 #node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name
528 #if isinstance(node, list) :
529 # node = node[0]
530 #self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data
531 #self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name
467532
533 rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', namespaces=ns)[0]
534 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
535 self.check_rate_date(rate_date_datetime, max_delta_days)
468 #we dynamically update supported currencies536 #we dynamically update supported currencies
469 self.supported_currency_array = []537 self.supported_currency_array = dom.xpath('/tabela_kursow/pozycja/kod_waluty/text()', namespaces=ns)
470 self.supported_currency_array.append('PLN')538 self.supported_currency_array.append('PLN')
471 for el in xpath.Evaluate("/tabela_kursow/pozycja/kod_waluty/text()", dom):539 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array))
472 self.supported_currency_array.append(el)
473 self.validate_cur(main_currency)540 self.validate_cur(main_currency)
541 if main_currency != 'PLN':
542 main_curr_data = self.rate_retrieve(dom, ns, main_currency)
543 # 1 MAIN_CURRENCY = main_rate PLN
544 main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref']
474 for curr in currency_array :545 for curr in currency_array :
475 curr_xpath = "/tabela_kursow/pozycja[kod_waluty='%s']"%(curr.upper())546 self.validate_cur(curr)
476 for node in xpath.Evaluate(curr_xpath, dom): 547 if curr == 'PLN':
477 tmp_data = self.rate_retrieve(node)548 rate = main_rate
478 #Source is in PLN, so we transform it into reference currencies549 else:
479 rate = 1 / (tmp_data['rate_currency'] / tmp_data['rate_ref'])550 curr_data = self.rate_retrieve(dom, ns, curr)
480 self.updated_currency[curr] = rate551 # 1 MAIN_CURRENCY = rate CURR
552 if main_currency == 'PLN':
553 rate = curr_data['rate_ref'] / curr_data['rate_currency']
554 else:
555 rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']
556 self.updated_currency[curr] = rate
557 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
481 return self.updated_currency, self.log_info558 return self.updated_currency, self.log_info
482559
=== modified file 'c2c_currency_rate_update/currency_rate_update.xml'
--- c2c_currency_rate_update/currency_rate_update.xml 2009-07-10 13:34:11 +0000
+++ c2c_currency_rate_update/currency_rate_update.xml 2010-12-27 13:29:09 +0000
@@ -23,9 +23,10 @@
23 <form string="Rate">23 <form string="Rate">
24 <field name="service"/>24 <field name="service"/>
25 <field name="company_id"/>25 <field name="company_id"/>
26 <separator string="Currencies to update with this services" colspan="4"/>26 <field name="max_delta_days"/>
27 <separator string="Currencies to update with this service" colspan="4"/>
27 <field name="currency_to_update" colspan="4" nolabel="1"/>28 <field name="currency_to_update" colspan="4" nolabel="1"/>
28 <separator string="note" colspan="4"/>29 <separator string="Logs" colspan="4"/>
29 <field name="note" colspan="4" nolabel="1"/> 30 <field name="note" colspan="4" nolabel="1"/>
30 </form>31 </form>
31 </field>32 </field>