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
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>