diff --git a/currency_rate_update/__openerp__.py b/currency_rate_update/__openerp__.py index 3fb6db666..2e8b25c06 100644 --- a/currency_rate_update/__openerp__.py +++ b/currency_rate_update/__openerp__.py @@ -22,11 +22,11 @@ # ############################################################################## { - "name" : "Currency Rate Update", - "version" : "0.7", - "author" : "Camptocamp", - "website" : "http://camptocamp.com", - "category" : "Financial Management/Configuration", + "name": "Currency Rate Update", + "version": "0.7", + "author": "Camptocamp", + "website": "http://camptocamp.com", + "category": "Financial Management/Configuration", "description": """Import exchange rates from the Internet. The module is able to use 4 different sources: @@ -48,7 +48,7 @@ The module is able to use 4 different sources: You should check when rates should apply to bookkeeping. If next day you should change the update hour in schedule settings because in OpenERP they apply from date of update (date - no hours). - + 5. Banxico for USD & MXN (created by Agustín Cruz) Updated daily @@ -64,7 +64,7 @@ The module uses internal ir_cron feature from OpenERP, so the job is launched on the server starts if the 'first execute date' is before the current day. The module supports multi-company currency in two ways: -* the currencies are shared, you can set currency update only on one +* the currencies are shared, you can set currency update only on one company * the currency are separated, you can set currency on every company separately @@ -76,16 +76,16 @@ found in database. Thanks to main contributors: Grzegorz Grzelak, Alexis de Lattre """, - "depends" : [ + "depends": [ "base", - "account", #Added to ensure account security groups are present - ], - "data" : [ + "account", # Added to ensure account security groups are present + ], + "data": [ "currency_rate_update.xml", "company_view.xml", "security/security.xml", - ], - "demo" : [], + ], + "demo": [], "active": False, 'installable': True } diff --git a/currency_rate_update/company.py b/currency_rate_update/company.py index b9d6b1303..b722983f5 100644 --- a/currency_rate_update/company.py +++ b/currency_rate_update/company.py @@ -18,17 +18,21 @@ # along with this program. If not, see . # ############################################################################## - -from openerp import netsvc from openerp.osv import fields, orm + class res_company(orm.Model): """override company to add currency update""" def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}): "check if multi company currency is enabled" result = {} - if self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', 'company_id'), ('model', '=', 'res.currency')])==[]: + fields = self.pool.get('ir.model.fields').search( + cr, uid, + [('name', '=', 'company_id'), + ('model', '=', 'res.currency')] + ) + if not fields: enable = 0 else: enable = 1 @@ -36,18 +40,15 @@ class res_company(orm.Model): result[id] = enable return result - def button_refresh_currency(self, cr, uid, ids, context=None): - """Refrech the currency !!for all the company - now""" + """Refrech the currency for all the company now""" currency_updater_obj = self.pool.get('currency.rate.update') try: currency_updater_obj.run_currency_update(cr, uid) - except Exception, e: + except Exception: return False return True - def on_change_auto_currency_up(self, cr, uid, id, value): """handle the activation of the currecny update on compagnies. There are two ways of implementing multi_company currency, @@ -56,102 +57,100 @@ class res_company(orm.Model): auto update on one company, this will avoid to have unusefull cron object running. If yours currency are not share you will be able to activate the - auto update on each separated company""" + auto update on each separated company - if len(id) : + """ + + if len(id): id = id[0] - else : + else: return {} enable = self.browse(cr, uid, id).multi_company_currency_enable - compagnies = self.search(cr, uid, []) + compagnies = self.search(cr, uid, []) activate_cron = 'f' - if not value : + if not value: # this statement is here beacaus we do no want to save in case of error - self.write(cr, uid, id,{'auto_currency_up':value}) - for comp in compagnies : + self.write(cr, uid, id, {'auto_currency_up': value}) + for comp in compagnies: if self.browse(cr, uid, comp).auto_currency_up: activate_cron = 't' break self.pool.get('currency.rate.update').save_cron( - cr, - uid, - {'active':activate_cron} - ) + cr, + uid, + {'active': activate_cron} + ) return {} - else : - for comp in compagnies : + else: + for comp in compagnies: if comp != id and not enable: if self.browse(cr, uid, comp).multi_company_currency_enable: #we ensure taht we did not have write a true value - self.write(cr, uid, id,{'auto_currency_up':False}) + self.write(cr, uid, id, {'auto_currency_up': False}) return { - 'value':{ - 'auto_currency_up':False - }, + 'value': {'auto_currency_up': False}, - 'warning':{ - 'title':"Warning", - 'message': 'Yon can not activate auto currency '+\ - 'update on more thant one company with this '+ - 'multi company configuration' - } - } - self.write(cr, uid, id,{'auto_currency_up':value}) - for comp in compagnies : + 'warning': { + 'title': "Warning", + 'message': 'You can not activate auto currency ' + 'update on more thant one company with this ' + 'multi company configuration' + } + } + self.write(cr, uid, id, {'auto_currency_up': value}) + for comp in compagnies: if self.browse(cr, uid, comp).auto_currency_up: activate_cron = 't' self.pool.get('currency.rate.update').save_cron( - cr, - uid, - {'active':activate_cron} - ) + cr, + uid, + {'active': activate_cron} + ) break return {} - - def on_change_intervall(self, cr, uid, id, interval) : - ###Function that will update the cron - ###freqeuence + def on_change_intervall(self, cr, uid, id, interval): + # Function that will update the cron freqeuence self.pool.get('currency.rate.update').save_cron( - cr, - uid, - {'interval_type':interval} - ) - compagnies = self.search(cr, uid, []) - for comp in compagnies : - self.write(cr, uid, comp,{'interval_type':interval}) + cr, + uid, + {'interval_type': interval} + ) + compagnies = self.search(cr, uid, []) + for comp in compagnies: + self.write(cr, uid, comp, {'interval_type': interval}) return {} _inherit = "res.company" _columns = { - ### activate the currency update - 'auto_currency_up': fields.boolean('Automatical update of the currency this company'), - 'services_to_use' : fields.one2many( - 'currency.rate.update.service', - 'company_id', - 'Currency update services' - ), - ###predifine cron frequence + # Activate the currency update + 'auto_currency_up': fields.boolean( + 'Automatical update of the currency this company' + ), + 'services_to_use': fields.one2many( + 'currency.rate.update.service', + 'company_id', + 'Currency update services' + ), + # Predifine cron frequence 'interval_type': fields.selection( - [ - ('days','Day(s)'), - ('weeks', 'Week(s)'), - ('months', 'Month(s)') - ], - 'Currency update frequence', - help="""changing this value will - also affect other compagnies""" - ), - ###function field that allows to know the - ###mutli company currency implementation - 'multi_company_currency_enable' : fields.function( - _multi_curr_enable, - method=True, - type='boolean', - string="Multi company currency", - help='if this case is not check you can'+\ - ' not set currency is active on two company' - ), + [ + ('days', 'Day(s)'), + ('weeks', 'Week(s)'), + ('months', 'Month(s)') + ], + 'Currency update frequence', + help="Changing this value will " + "also affect other compagnies" + ), + # Function field that allows to know the + # mutli company currency implementation + 'multi_company_currency_enable': fields.function( + _multi_curr_enable, + method=True, + type='boolean', + string="Multi company currency", + help="If this case is not check you can" + " not set currency is active on two company" + ), } - - diff --git a/currency_rate_update/currency_rate_update.py b/currency_rate_update/currency_rate_update.py index b29e08f5c..4f3023dde 100644 --- a/currency_rate_update/currency_rate_update.py +++ b/currency_rate_update/currency_rate_update.py @@ -2,7 +2,6 @@ ############################################################################## # # Copyright (c) 2009 Camptocamp SA -# @author Nicolas Bessi # @source JBA and AWST inpiration # @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), Joel Grand-Guillaume # Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr) @@ -31,68 +30,75 @@ # ############################################################################## -# TODO "nice to have" : restain the list of currencies that can be added for +# TODO "nice to have" : restrain the list of currencies that can be added for # a webservice to the list of currencies supported by the Webservice # TODO : implement max_delta_days for Yahoo webservice -from openerp.osv import fields, osv, orm +import logging import time from datetime import datetime, timedelta -import logging +from openerp.osv import fields, osv, orm from openerp.tools.translate import _ _logger = logging.getLogger(__name__) + class Currency_rate_update_service(osv.Model): - """Class thats tell for wich services wich currencies - have to be updated""" + """Class that tells for wich services wich currencies have to be updated + + """ _name = "currency.rate.update.service" _description = "Currency Rate Update" _columns = { - ##list of webservicies the value sould be a class name - 'service' : fields.selection( - [ - ('Admin_ch_getter','Admin.ch'), - ('ECB_getter','European Central Bank'), - #('NYFB_getter','Federal Reserve Bank of NY'), - #('Google_getter','Google Finance'), - ('Yahoo_getter','Yahoo Finance '), - ('PL_NBP_getter','Narodowy Bank Polski'), # Added for polish rates - ('Banxico_getter', 'Banco de México'), # Added for mexican rates - # Bank of Canada is using RSS-CB http://www.cbwiki.net/wiki/index.php/Specification_1.1 : - # This RSS format is used by other national banks (Thailand, Malaysia, Mexico...) - ('CA_BOC_getter','Bank of Canada - noon rates'), # Added for canadian rates - ], - "Webservice to use", - required = True - ), - ##list of currency to update - 'currency_to_update' : fields.many2many( - 'res.currency', - 'res_curreny_auto_udate_rel', - 'service_id', - 'currency_id', - 'currency to update with this service', - ), - #back ref - 'company_id' : fields.many2one( - 'res.company', - 'linked company', - ), - ##note fileds that will be used as a logger - 'note':fields.text('update notice'), - '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."), - } - _defaults = { - 'max_delta_days': lambda *a: 4, + # List of webservicies the value sould be a class name + 'service': fields.selection( + [ + ('Admin_ch_getter', 'Admin.ch'), + ('ECB_getter', 'European Central Bank'), + #('NYFB_getter','Federal Reserve Bank of NY'), + #('Google_getter','Google Finance'), + ('Yahoo_getter', 'Yahoo Finance '), + ('PL_NBP_getter', 'Narodowy Bank Polski'), # Added for polish rates + ('Banxico_getter', 'Banco de México'), # Added for mexican rates + # Bank of Canada is using RSS-CB http://www.cbwiki.net/wiki/index.php/Specification_1.1 : + # This RSS format is used by other national banks (Thailand, Malaysia, Mexico...) + ('CA_BOC_getter', 'Bank of Canada - noon rates'), # Added for canadian rates + ], + "Webservice to use", + required=True + ), + # List of currency to update + 'currency_to_update': fields.many2many( + 'res.currency', + 'res_curreny_auto_udate_rel', + 'service_id', + 'currency_id', + 'currency to update with this service', + ), + # Back ref + 'company_id': fields.many2one( + 'res.company', + 'linked company', + ), + # Note fileds that will be used as a logger + 'note': fields.text('update notice'), + '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." + ), } + _defaults = {'max_delta_days': lambda *a: 4} _sql_constraints = [ - ( - 'curr_service_unique', - 'unique (service, company_id)', - _('You can use a service one time per company !') - ) - ] + ( + 'curr_service_unique', + 'unique (service, company_id)', + _('You can use a service one time per company !') + ) + ] def _check_max_delta_days(self, cr, uid, ids): for company in self.read(cr, uid, ids, ['max_delta_days']): @@ -103,7 +109,9 @@ class Currency_rate_update_service(osv.Model): return True _constraints = [ - (_check_max_delta_days, "'Max delta days' must be >= 0", ['max_delta_days']), + (_check_max_delta_days, + "'Max delta days' must be >= 0", + ['max_delta_days']), ] @@ -112,58 +120,59 @@ class Currency_rate_update(osv.Model): update currencies based on a web url""" _name = "currency.rate.update" _description = "Currency Rate Update" - ##dict that represent a cron object + # Dict that represent a cron object cron = { - 'active' : False, - 'priority' : 1, - 'interval_number' : 1, - 'interval_type' : 'weeks', - 'nextcall' : time.strftime("%Y-%m-%d %H:%M:%S", (datetime.today() + timedelta(days=1)).timetuple() ), #tomorrow same time - 'numbercall' : -1, - 'doall' : True, - 'model' : 'currency.rate.update', - 'function' : 'run_currency_update', - 'args' : '()', + 'active': False, + 'priority': 1, + 'interval_number': 1, + 'interval_type': 'weeks', + 'nextcall': time.strftime("%Y-%m-%d %H:%M:%S", + datetime.today() + timedelta(days=1)).timetuple(), + 'numbercall': -1, + 'doall': True, + 'model': 'currency.rate.update', + 'function': 'run_currency_update', + 'args': '()', } LOG_NAME = 'cron-rates' MOD_NAME = 'currency_rate_update: ' + def get_cron_id(self, cr, uid, context): - """return the updater cron's id. Create one if the cron does not exists """ + """Returns the updater cron's id. + Create one if the cron does not exists + """ cron_id = 0 cron_obj = self.pool.get('ir.cron') try: - #find the cron that send messages + #Finds the cron that send messages cron_id = cron_obj.search( - cr, - uid, - [ - ('function', 'ilike', self.cron['function']), - ('model', 'ilike', self.cron['model']) - ], - context={ - 'active_test': False - } - ) + cr, + uid, + [ + ('function', 'ilike', self.cron['function']), + ('model', 'ilike', self.cron['model']) + ], + context={ + 'active_test': False + } + ) cron_id = int(cron_id[0]) - except Exception,e : + except Exception: _logger.info('warning cron not found one will be created') - pass # ignore if the cron is missing cause we are going to create it in db + pass # Ignore if the cron is missing cause we are going to create it in db - #the cron does not exists - if not cron_id : - #translate + #The cron does not exists + if not cron_id: self.cron['name'] = _('Currency Rate Update') cron_id = cron_obj.create(cr, uid, self.cron, context) - return cron_id def save_cron(self, cr, uid, datas, context={}): """save the cron config data should be a dict""" - #modify the cron cron_id = self.get_cron_id(cr, uid, context) - result = self.pool.get('ir.cron').write(cr, uid, [cron_id], datas) + return self.pool.get('ir.cron').write(cr, uid, [cron_id], datas) def run_currency_update(self, cr, uid): "update currency at the given frequence" @@ -172,123 +181,145 @@ class Currency_rate_update(osv.Model): rate_obj = self.pool.get('res.currency.rate') companies = self.pool.get('res.company').search(cr, uid, []) for comp in self.pool.get('res.company').browse(cr, uid, companies): - ##the multi company currency can beset or no so we handle - ##the two case - if not comp.auto_currency_up : + #The multi company currency can beset or no so we handle + #The two case + if not comp.auto_currency_up: continue - #we initialise the multi compnay search filter or not serach filter - search_filter = [] - if comp.multi_company_currency_enable : - search_filter = [('company_id','=',comp.id)] - #we fetch the main currency looking for currency with base = true. The main rate should be set at 1.00 - main_curr_ids = curr_obj.search(cr, uid, [('base','=',True),('company_id','=',comp.id)]) + #We fetch the main currency looking for currency with base = true. + #The main rate should be set at 1.00 + main_curr_ids = curr_obj.search( + cr, uid, + [('base', '=', True), ('company_id', '=', comp.id)] + ) if not main_curr_ids: - # If we can not find a base currency for this company we look for one with no company set - main_curr_ids = curr_obj.search(cr, uid, [('base','=',True),('company_id','=', False)]) + # If we can not find a base currency for this company + # we look for one with no company set + main_curr_ids = curr_obj.search( + cr, uid, + [('base', '=', True), ('company_id', '=', False)] + ) if main_curr_ids: main_curr_rec = curr_obj.browse(cr, uid, main_curr_ids[0]) else: - raise orm.except_orm(_('Error!'),('There is no base currency set!')) + raise orm.except_orm( + _('Error!'), + ('There is no base currency set!') + ) if main_curr_rec.rate != 1: - raise orm.except_orm(_('Error!'),('Base currency rate should be 1.00!')) + raise orm.except_orm( + _('Error!'), + ('Base currency rate should be 1.00!') + ) main_curr = main_curr_rec.name - for service in comp.services_to_use : - print "comp.services_to_use =", comp.services_to_use + for service in comp.services_to_use: note = service.note or '' - try : - ## we initalize the class that will handle the request - ## and return a dict of rate + try: + #We initalize the class that will handle the request + #and return a dict of rate getter = factory.register(service.service) - print "getter =", getter - curr_to_fetch = map(lambda x : x.name, service.currency_to_update) - res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr, service.max_delta_days) + curr_to_fetch = map(lambda x: x.name, service.currency_to_update) + res, log_info = getter.get_updated_currency( + curr_to_fetch, + main_curr, + service.max_delta_days + ) rate_name = time.strftime('%Y-%m-%d') - for curr in service.currency_to_update : - if curr.name == main_curr : + for curr in service.currency_to_update: + if curr.name == main_curr: continue do_create = True - for rate in curr.rate_ids : - if rate.name == rate_name : - rate.write({'rate':res[curr.name]}) + for rate in curr.rate_ids: + if rate.name == rate_name: + rate.write({'rate': res[curr.name]}) do_create = False break - if do_create : + if do_create: vals = { - 'currency_id': curr.id, - 'rate':res[curr.name], - 'name': rate_name - } + 'currency_id': curr.id, + 'rate': res[curr.name], + 'name': rate_name + } rate_obj.create( - cr, - uid, - vals, - ) + cr, + uid, + vals, + ) - # show the most recent note at the top - note = "\n%s currency updated. "\ - %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'))\ - + note - note = (log_info or '') + note - service.write({'note':note}) - except Exception, e: - error_msg = "\n%s ERROR : %s"\ - %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), str(e))\ - + note - _logger.info(str(e)) - service.write({'note':error_msg}) + # Show the most recent note at the top + msg = "%s \n%s currency updated. %s" % \ + (log_info or '', + datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), + note) + service.write({'note': msg}) + except Exception as exc: + error_msg = "\n%s ERROR : %s %s" %\ + (datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), + repr(exc), + note) + _logger.info(repr(exc)) + service.write({'note': error_msg}) - -### Error Definition as specified in python 2.6 PEP class AbstractClassError(Exception): def __str__(self): return 'Abstract Class' + def __repr__(self): - return 'Abstract Class' + return 'Abstract Class' + class AbstractMethodError(Exception): def __str__(self): return 'Abstract Method' + def __repr__(self): return 'Abstract Method' + class UnknowClassError(Exception): def __str__(self): return 'Unknown Class' + def __repr__(self): return 'Unknown Class' + + class UnsuportedCurrencyError(Exception): def __init__(self, value): - self.curr = value - def __str__(self): - return 'Unsupported currency '+self.curr - def __repr__(self): - return 'Unsupported currency '+self.curr + self.curr = value + + def __str__(self): + return 'Unsupported currency %s' % self.curr + + def __repr__(self): + return 'Unsupported currency %s' % self.curr + -### end of error definition class Currency_getter_factory(): """Factory pattern class that will return a currency getter class base on the name passed - to the register method""" + to the register method + + """ def register(self, class_name): allowed = [ - 'Admin_ch_getter', - 'PL_NBP_getter', - 'ECB_getter', - 'NYFB_getter', - 'Google_getter', - 'Yahoo_getter', - 'Banxico_getter', - 'CA_BOC_getter', - ] + 'Admin_ch_getter', + 'PL_NBP_getter', + 'ECB_getter', + 'NYFB_getter', + 'Google_getter', + 'Yahoo_getter', + 'Banxico_getter', + 'CA_BOC_getter', + ] if class_name in allowed: class_def = eval(class_name) return class_def() - else : + else: raise UnknowClassError -class Curreny_getter_interface(object) : +class Curreny_getter_interface(object): "Abstract class of currency getter" #remove in order to have a dryer code @@ -297,47 +328,49 @@ class Curreny_getter_interface(object) : log_info = " " - supported_currency_array = \ -['AFN', 'ALL', 'DZD', 'USD', 'USD', 'USD', 'EUR', 'AOA', 'XCD', 'XCD', 'ARS', -'AMD', 'AWG', 'AUD', 'EUR', 'AZN', 'EUR', 'BSD', 'BHD', 'EUR', 'BDT', 'BBD', -'XCD', 'BYR', 'EUR', 'BZD', 'XOF', 'BMD', 'BTN', 'INR', 'BOB', 'ANG', 'BAM', -'BWP', 'NOK', 'BRL', 'GBP', 'USD', 'USD', 'BND', 'BGN', 'XOF', 'MMK', 'BIF', -'XOF', 'USD', 'KHR', 'XAF', 'CAD', 'EUR', 'CVE', 'KYD', 'XAF', 'XAF', 'CLP', -'CNY', 'AUD', 'AUD', 'COP', 'XAF', 'KMF', 'XPF', 'XAF', 'CDF', 'NZD', 'CRC', -'HRK', 'CUP', 'ANG', 'EUR', 'CYP', 'CZK', 'DKK', 'DJF', 'XCD', 'DOP', 'EUR', -'XCD', 'IDR', 'USD', 'EGP', 'EUR', 'SVC', 'USD', 'GBP', 'XAF', 'ETB', 'ERN', -'EEK', 'ETB', 'EUR', 'FKP', 'DKK', 'FJD', 'EUR', 'EUR', 'EUR', 'XPF', 'XPF', -'EUR', 'XPF', 'XAF', 'GMD', 'GEL', 'EUR', 'GHS', 'GIP', 'XAU', 'GBP', 'EUR', -'DKK', 'XCD', 'XCD', 'EUR', 'USD', 'GTQ', 'GGP', 'GNF', 'XOF', 'GYD', 'HTG', -'USD', 'AUD', 'BAM', 'EUR', 'EUR', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR', -'XDR', 'IRR', 'IQD', 'EUR', 'IMP', 'ILS', 'EUR', 'JMD', 'NOK', 'JPY', 'JEP', -'JOD', 'KZT', 'AUD', 'KES', 'AUD', 'KPW', 'KRW', 'KWD', 'KGS', 'LAK', 'LVL', -'LBP', 'LSL', 'ZAR', 'LRD', 'LYD', 'CHF', 'LTL', 'EUR', 'MOP', 'MKD', 'MGA', -'EUR', 'MWK', 'MYR', 'MVR', 'XOF', 'EUR', 'MTL', 'FKP', 'USD', 'USD', 'EUR', -'MRO', 'MUR', 'EUR', 'AUD', 'MXN', 'USD', 'USD', 'EUR', 'MDL', 'EUR', 'MNT', -'EUR', 'XCD', 'MAD', 'MZN', 'MMK', 'NAD', 'ZAR', 'AUD', 'NPR', 'ANG', 'EUR', -'XCD', 'XPF', 'NZD', 'NIO', 'XOF', 'NGN', 'NZD', 'AUD', 'USD', 'NOK', 'OMR', -'PKR', 'USD', 'XPD', 'PAB', 'USD', 'PGK', 'PYG', 'PEN', 'PHP', 'NZD', 'XPT', -'PLN', 'EUR', 'STD', 'USD', 'QAR', 'EUR', 'RON', 'RUB', 'RWF', 'STD', 'ANG', -'MAD', 'XCD', 'SHP', 'XCD', 'XCD', 'EUR', 'XCD', 'EUR', 'USD', 'WST', 'EUR', -'SAR', 'SPL', 'XOF', 'RSD', 'SCR', 'SLL', 'XAG', 'SGD', 'ANG', 'ANG', 'EUR', -'EUR', 'SBD', 'SOS', 'ZAR', 'GBP', 'GBP', 'EUR', 'XDR', 'LKR', 'SDG', 'SRD', -'NOK', 'SZL', 'SEK', 'CHF', 'SYP', 'TWD', 'RUB', 'TJS', 'TZS', 'THB', 'IDR', -'TTD', 'XOF', 'NZD', 'TOP', 'TTD', 'TND', 'TRY', 'TMM', 'USD', 'TVD', 'UGX', -'UAH', 'AED', 'GBP', 'USD', 'USD', 'UYU', 'USD', 'UZS', 'VUV', 'EUR', 'VEB', -'VEF', 'VND', 'USD', 'USD', 'USD', 'XPF', 'MAD', 'YER', 'ZMK', 'ZWD'] + supported_currency_array = [ + 'AFN', 'ALL', 'DZD', 'USD', 'USD', 'USD', 'EUR', 'AOA', 'XCD', 'XCD', 'ARS', + 'AMD', 'AWG', 'AUD', 'EUR', 'AZN', 'EUR', 'BSD', 'BHD', 'EUR', 'BDT', 'BBD', + 'XCD', 'BYR', 'EUR', 'BZD', 'XOF', 'BMD', 'BTN', 'INR', 'BOB', 'ANG', 'BAM', + 'BWP', 'NOK', 'BRL', 'GBP', 'USD', 'USD', 'BND', 'BGN', 'XOF', 'MMK', 'BIF', + 'XOF', 'USD', 'KHR', 'XAF', 'CAD', 'EUR', 'CVE', 'KYD', 'XAF', 'XAF', 'CLP', + 'CNY', 'AUD', 'AUD', 'COP', 'XAF', 'KMF', 'XPF', 'XAF', 'CDF', 'NZD', 'CRC', + 'HRK', 'CUP', 'ANG', 'EUR', 'CYP', 'CZK', 'DKK', 'DJF', 'XCD', 'DOP', 'EUR', + 'XCD', 'IDR', 'USD', 'EGP', 'EUR', 'SVC', 'USD', 'GBP', 'XAF', 'ETB', 'ERN', + 'EEK', 'ETB', 'EUR', 'FKP', 'DKK', 'FJD', 'EUR', 'EUR', 'EUR', 'XPF', 'XPF', + 'EUR', 'XPF', 'XAF', 'GMD', 'GEL', 'EUR', 'GHS', 'GIP', 'XAU', 'GBP', 'EUR', + 'DKK', 'XCD', 'XCD', 'EUR', 'USD', 'GTQ', 'GGP', 'GNF', 'XOF', 'GYD', 'HTG', + 'USD', 'AUD', 'BAM', 'EUR', 'EUR', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR', + 'XDR', 'IRR', 'IQD', 'EUR', 'IMP', 'ILS', 'EUR', 'JMD', 'NOK', 'JPY', 'JEP', + 'JOD', 'KZT', 'AUD', 'KES', 'AUD', 'KPW', 'KRW', 'KWD', 'KGS', 'LAK', 'LVL', + 'LBP', 'LSL', 'ZAR', 'LRD', 'LYD', 'CHF', 'LTL', 'EUR', 'MOP', 'MKD', 'MGA', + 'EUR', 'MWK', 'MYR', 'MVR', 'XOF', 'EUR', 'MTL', 'FKP', 'USD', 'USD', 'EUR', + 'MRO', 'MUR', 'EUR', 'AUD', 'MXN', 'USD', 'USD', 'EUR', 'MDL', 'EUR', 'MNT', + 'EUR', 'XCD', 'MAD', 'MZN', 'MMK', 'NAD', 'ZAR', 'AUD', 'NPR', 'ANG', 'EUR', + 'XCD', 'XPF', 'NZD', 'NIO', 'XOF', 'NGN', 'NZD', 'AUD', 'USD', 'NOK', 'OMR', + 'PKR', 'USD', 'XPD', 'PAB', 'USD', 'PGK', 'PYG', 'PEN', 'PHP', 'NZD', 'XPT', + 'PLN', 'EUR', 'STD', 'USD', 'QAR', 'EUR', 'RON', 'RUB', 'RWF', 'STD', 'ANG', + 'MAD', 'XCD', 'SHP', 'XCD', 'XCD', 'EUR', 'XCD', 'EUR', 'USD', 'WST', 'EUR', + 'SAR', 'SPL', 'XOF', 'RSD', 'SCR', 'SLL', 'XAG', 'SGD', 'ANG', 'ANG', 'EUR', + 'EUR', 'SBD', 'SOS', 'ZAR', 'GBP', 'GBP', 'EUR', 'XDR', 'LKR', 'SDG', 'SRD', + 'NOK', 'SZL', 'SEK', 'CHF', 'SYP', 'TWD', 'RUB', 'TJS', 'TZS', 'THB', 'IDR', + 'TTD', 'XOF', 'NZD', 'TOP', 'TTD', 'TND', 'TRY', 'TMM', 'USD', 'TVD', 'UGX', + 'UAH', 'AED', 'GBP', 'USD', 'USD', 'UYU', 'USD', 'UZS', 'VUV', 'EUR', 'VEB', + 'VEF', 'VND', 'USD', 'USD', 'USD', 'XPF', 'MAD', 'YER', 'ZMK', 'ZWD' + ] - ##updated currency this arry will contain the final result + #Updated currency this arry will contain the final result updated_currency = {} - def get_updated_currency(self, currency_array, main_currency, max_delta_days) : + def get_updated_currency(self, currency_array, main_currency, max_delta_days): """Interface method that will retrieve the currency - This function has to be reinplemented in child""" + This function has to be reinplemented in child + """ raise AbstractMethodError - def validate_cur(self, currency) : + def validate_cur(self, currency): """Validate if the currency to update is supported""" - if currency not in self.supported_currency_array : + if currency not in self.supported_currency_array: raise UnsuportedCurrencyError(currency) def get_url(self, url): @@ -349,65 +382,83 @@ class Curreny_getter_interface(object) : objfile.close() return rawfile except ImportError: - raise osv.except_osv('Error !', self.MOD_NAME+'Unable to import urllib !') + raise osv.except_osv( + 'Error !', + self.MOD_NAME + 'Unable to import urllib !' + ) except IOError: - raise osv.except_osv('Error !', self.MOD_NAME+'Web Service does not exist !') + raise osv.except_osv( + 'Error !', + self.MOD_NAME + 'Web Service does not exist !' + ) def check_rate_date(self, rate_date, max_delta_days): - """Check date constrains. WARN : rate_date must be of datetime type""" + """Check date constrains. rate_date must be of datetime type""" days_delta = (datetime.today() - rate_date).days if days_delta > max_delta_days: - raise Exception('The rate timestamp (%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)) - # We always have a warning when rate_date <> today + raise Exception( + 'The rate timestamp (%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) + ) + + #We always have a warning when rate_date != today rate_date_str = datetime.strftime(rate_date, '%Y-%m-%d') if rate_date_str != datetime.strftime(datetime.today(), '%Y-%m-%d'): - self.log_info = "WARNING : the rate timestamp (%s) is not today's date" % rate_date_str - _logger.warning("the rate timestamp (%s) is not today's date", rate_date_str) + msg = "The rate timestamp (%s) is not today's date" + self.log_info = ("WARNING : %s %s") % (msg, rate_date_str) + _logger.warning(msg, rate_date_str) -#Yahoo ################################################################################### -class Yahoo_getter(Curreny_getter_interface) : +#Yahoo ######################################################################## +class Yahoo_getter(Curreny_getter_interface): """Implementation of Currency_getter_factory interface - for Yahoo finance service""" + for Yahoo finance service + """ def get_updated_currency(self, currency_array, main_currency, max_delta_days): - """implementation of abstract method of Curreny_getter_interface""" + """implementation of abstract method of curreny_getter_interface""" self.validate_cur(main_currency) - url='http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg' - if main_currency in currency_array : + url = 'http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg' + if main_currency in currency_array: currency_array.remove(main_currency) - for curr in currency_array : + for curr in currency_array: self.validate_cur(curr) - res = self.get_url(url%(main_currency+curr)) + res = self.get_url(url % (main_currency + curr)) val = res.split(',')[1] - if val : + if val: self.updated_currency[curr] = val - else : - raise Exception('Could not update the %s'%(curr)) + else: + raise Exception('Could not update the %s' % (curr)) - return self.updated_currency, self.log_info # empty string added by polish changes -##Admin CH ############################################################################ -class Admin_ch_getter(Curreny_getter_interface) : + return self.updated_currency, self.log_info + + +#Admin CH ##################################################################### +class Admin_ch_getter(Curreny_getter_interface): """Implementation of Currency_getter_factory interface - for Admin.ch service""" + for Admin.ch service - def rate_retrieve(self, dom, ns, curr) : - """ Parse a dom node to retrieve- - currencies data""" + """ + + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve currencies data""" res = {} - xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()"%(curr.lower()) - xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()"%(curr.lower()) + xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()" % (curr.lower()) + xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()" % (curr.lower()) res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0]) res['rate_ref'] = float((dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[0]) return res def get_updated_currency(self, currency_array, main_currency, max_delta_days): - """implementation of abstract method of Curreny_getter_interface""" - url='http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php' - #we do not want to update the main currency - if main_currency in currency_array : + """Implementation of abstract method of Curreny_getter_interface""" + url = 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php' + #We do not want to update the main currency + if main_currency in currency_array: currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 + #Move to new XML lib cf Launchpad bug #645263 from lxml import etree _logger.debug("Admin.ch currency rate service : connecting...") rawfile = self.get_url(url) @@ -428,60 +479,70 @@ class Admin_ch_getter(Curreny_getter_interface) : main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) # 1 MAIN_CURRENCY = main_rate CHF main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref'] - for curr in currency_array : + for curr in currency_array: self.validate_cur(curr) if curr == 'CHF': rate = main_rate else: curr_data = self.rate_retrieve(dom, adminch_ns, curr) # 1 MAIN_CURRENCY = rate CURR - if main_currency == 'CHF' : + if main_currency == 'CHF': rate = curr_data['rate_ref'] / curr_data['rate_currency'] - else : + else: rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency'] self.updated_currency[curr] = rate - _logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr) + _logger.debug( + "Rate retrieved : 1 %s = %s %s" % (main_currency, rate, curr) + ) return self.updated_currency, self.log_info -## ECB getter ############################################################################ -class ECB_getter(Curreny_getter_interface) : +## ECB getter ################################################################# +class ECB_getter(Curreny_getter_interface): """Implementation of Currency_getter_factory interface - for ECB service""" + for ECB service + """ - def rate_retrieve(self, dom, ns, curr) : - """ Parse a dom node to retrieve- - currencies data""" + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve- + currencies data + + """ res = {} - xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate"%(curr.upper()) + xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate" % (curr.upper()) res['rate_currency'] = float(dom.xpath(xpath_curr_rate, namespaces=ns)[0]) return res def get_updated_currency(self, currency_array, main_currency, max_delta_days): """implementation of abstract method of Curreny_getter_interface""" - url='http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' + url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' # Important : as explained on the ECB web site, the currencies are # at the beginning of the afternoon ; so, until 3 p.m. Paris time # the currency rates are the ones of trading day N-1 # see http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html - #we do not want to update the main currency - if main_currency in currency_array : + #We do not want to update the main currency + if main_currency in currency_array: currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 + #Move to new XML lib cf Launchpad bug #645263 from lxml import etree _logger.debug("ECB currency rate service : connecting...") rawfile = self.get_url(url) dom = etree.fromstring(rawfile) _logger.debug("ECB sent a valid XML file") - ecb_ns = {'gesmes': 'http://www.gesmes.org/xml/2002-08-01', 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'} - rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', namespaces=ecb_ns)[0] + ecb_ns = { + 'gesmes': 'http://www.gesmes.org/xml/2002-08-01', + 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref' + } + rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', + namespaces=ecb_ns)[0] rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d') self.check_rate_date(rate_date_datetime, max_delta_days) #we dynamically update supported currencies - self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", namespaces=ecb_ns) + self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", + namespaces=ecb_ns) self.supported_currency_array.append('EUR') - _logger.debug("Supported currencies = " + str(self.supported_currency_array)) + _logger.debug("Supported currencies = %s " % self.supported_currency_array) self.validate_cur(main_currency) if main_currency != 'EUR': main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency) @@ -496,56 +557,53 @@ class ECB_getter(Curreny_getter_interface) : else: rate = curr_data['rate_currency'] / main_curr_data['rate_currency'] self.updated_currency[curr] = rate - _logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr) + _logger.debug("Rate retrieved : 1 %s = %s %s" % (main_currency, rate, curr)) return self.updated_currency, self.log_info -##PL NBP ############################################################################ -class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter - """Implementation of Currency_getter_factory interface - for PL NBP service""" - def rate_retrieve(self, dom, ns, curr) : +#PL NBP ####################################################################### +class PL_NBP_getter(Curreny_getter_interface): + """Implementation of Currency_getter_factory interface + for PL NBP service + + """ + + def rate_retrieve(self, dom, ns, curr): """ Parse a dom node to retrieve currencies data""" res = {} - xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()"%(curr.upper()) - xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()"%(curr.upper()) - res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',','.')) + xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()" % (curr.upper()) + xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()" % (curr.upper()) + res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.')) res['rate_ref'] = float(dom.xpath(xpath_rate_ref, namespaces=ns)[0]) return res def get_updated_currency(self, currency_array, main_currency, max_delta_days): """implementation of abstract method of Curreny_getter_interface""" - url='http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one + url = 'http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one #we do not want to update the main currency - if main_currency in currency_array : + if main_currency in currency_array: currency_array.remove(main_currency) # Move to new XML lib cf Launchpad bug #645263 from lxml import etree _logger.debug("NBP.pl currency rate service : connecting...") rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) # If rawfile is not XML, it crashes here - ns = {} # Cool, there are no namespaces ! + dom = etree.fromstring(rawfile) + ns = {} # Cool, there are no namespaces ! _logger.debug("NBP.pl sent a valid XML file") - #node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name - #if isinstance(node, list) : - # node = node[0] - #self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data - #self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name - rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', namespaces=ns)[0] rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d') self.check_rate_date(rate_date_datetime, max_delta_days) #we dynamically update supported currencies self.supported_currency_array = dom.xpath('/tabela_kursow/pozycja/kod_waluty/text()', namespaces=ns) self.supported_currency_array.append('PLN') - _logger.debug("Supported currencies = " + str(self.supported_currency_array)) + _logger.debug("Supported currencies = %s" % self.supported_currency_array) self.validate_cur(main_currency) if main_currency != 'PLN': main_curr_data = self.rate_retrieve(dom, ns, main_currency) # 1 MAIN_CURRENCY = main_rate PLN main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref'] - for curr in currency_array : + for curr in currency_array: self.validate_cur(curr) if curr == 'PLN': rate = main_rate @@ -557,14 +615,16 @@ class PL_NBP_getter(Curreny_getter_interface) : # class added according to pol else: rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency'] self.updated_currency[curr] = rate - _logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr) + _logger.debug("Rate retrieved : %s = %s %s" % (main_currency, rate, curr)) return self.updated_currency, self.log_info -##Banco de México ############################################################################ -class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates +##Banco de México ############################################################# +class Banxico_getter(Curreny_getter_interface): """Implementation of Currency_getter_factory interface - for Banco de México service""" + for Banco de México service + + """ def rate_retrieve(self): """ Get currency exchange from Banxico.xml and proccess it @@ -587,17 +647,16 @@ class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates return float(rate) - def get_updated_currency(self, currency_array, main_currency, max_delta_days=1): """implementation of abstract method of Curreny_getter_interface""" logger = logging.getLogger(__name__) # we do not want to update the main currency - if main_currency in currency_array : + if main_currency in currency_array: currency_array.remove(main_currency) # Suported currencies suported = ['MXN', 'USD'] - for curr in currency_array : + for curr in currency_array: if curr in suported: # Get currency data main_rate = self.rate_retrieve() @@ -606,17 +665,19 @@ class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates else: rate = main_rate else: - """ No other currency supported - """ + # No other currency supported continue self.updated_currency[curr] = rate - logger.debug("Rate retrieved : " + main_currency + ' = ' + str(rate) + ' ' + curr) + logger.debug("Rate retrieved : %s = %s %s" % (main_currency, rate, curr)) -##CA BOC ##### Bank of Canada ############################################################ -class CA_BOC_getter(Curreny_getter_interface) : - """Implementation of Curreny_getter_factory interface for Bank of Canada RSS service""" +##CA BOC ##### Bank of Canada ############################################# +class CA_BOC_getter(Curreny_getter_interface): + """Implementation of Curreny_getter_factory interface + for Bank of Canada RSS service + + """ def get_updated_currency(self, currency_array, main_currency, max_delta_days): """implementation of abstract method of Curreny_getter_interface""" @@ -643,7 +704,7 @@ class CA_BOC_getter(Curreny_getter_interface) : self.validate_cur(curr) # check if BOC service is running - if dom.bozo and dom.status <> 404: + if dom.bozo and dom.status != 404: _logger.error("Bank of Canada - service is down - try again\ later...") @@ -664,8 +725,7 @@ class CA_BOC_getter(Curreny_getter_interface) : .astimezone(pytz.utc).replace(tzinfo=None) self.check_rate_date(rate_date_datetime, max_delta_days) self.updated_currency[curr] = rate - _logger.debug("BOC Rate retrieved : 1 " + main_currency + - ' = ' + str(rate) + ' ' + curr) + _logger.debug("BOC Rate retrieved : %s = %s %s" % (main_currency, rate, curr)) else: _logger.error("Exchange data format error for Bank of Canada -\ %s. Please check provider data format and/or source code." % curr)