# -*- coding: utf-8 -*- from collections import OrderedDict from xml.etree import ElementTree as ET from datetime import datetime from datetime import date from datetime import timedelta from suds.client import Client from suds.xsd.doctor import ImportDoctor, Import # import requests from django.db.models.sql.where import AND, OR from django.utils.translation import ugettext as _ from django.conf import settings from django.core.cache import cache from functions.model_utils import EnumChoices members_mapping = OrderedDict([ ('N200', {'min': None, 'max': 200, 'value': 1, 'label': _(u'до 200')}), ('N200500', {'min': 200, 'max': 400, 'value': 2, 'label': _(u'200-500')}), ('N5001000', {'min': 500, 'max': 500, 'value': 3, 'label': _(u'500-1000')}), ('N10002000', {'min': 1000, 'max': 2000, 'value': 4, 'label': _(u'1000-2000')}), ('N2000', {'min': 2000, 'max': None, 'value': 5, 'label': _(u'более 2000')}), ]) visitors_mapping = OrderedDict([ ('N5', {'min': None, 'max': 5000, 'value': 1, 'label': _(u'до 5 000')}), ('N510', {'min': 5000, 'max': 10000, 'value': 2, 'label': _(u'5 000 - 10 000')}), ('N1030', {'min': 10000, 'max': 30000, 'value': 3, 'label': _(u'10 000 - 30 000')}), ('N3050', {'min': 30000, 'max': 50000, 'value': 4, 'label': _(u'30 000 - 50 000')}), ('N50100', {'min': 50000, 'max': 100000, 'value': 5, 'label': _(u'50 000 - 100 000')}), ('N100', {'min': 100000, 'max': None, 'value': 6, 'label': _(u'более 100 000')}), ]) price_mapping = OrderedDict([ ('N0', {'min': None, 'max': None, 'value': 0, 'label': _(u'Не указана')}), ('N5', {'min': 1, 'max': 5000, 'value': 1, 'label': _(u'до 5000 руб.')}), ('N510', {'min': 5000, 'max': 10000, 'value': 2, 'label': _(u'5000-10000 руб.')}), ('N1020', {'min': 10000, 'max': 20000, 'value': 3, 'label': _(u'10000-20000 руб.')}), ('N20', {'min': 20000, 'max': None, 'value': 4, 'label': _(u'более 20000 руб.')}), ]) price_mapping_eur = OrderedDict([ ('N0', {'min': None, 'max': None, 'value': 0, 'label': _(u'Не указана')}), ('N5', {'min': 1, 'max': 100, 'value': 1, 'label': _(u'до 100 евро')}), ('N510', {'min': 100, 'max': 200, 'value': 2, 'label': _(u'100-200 евро')}), ('N1020', {'min': 200, 'max': 400, 'value': 3, 'label': _(u'200-400 евро')}), ('N20', {'min': 400, 'max': None, 'value': 4, 'label': _(u'более 400 евро')}), ]) def get_choices_kwargs(mapping): kwargs = OrderedDict() for key, val in mapping.iteritems(): kwargs[key] = (val.get('value'), val.get('label')) return kwargs MEMBERS = EnumChoices(**get_choices_kwargs(members_mapping)) VISITORS = EnumChoices(**get_choices_kwargs(visitors_mapping)) PRICE = EnumChoices(**get_choices_kwargs(price_mapping)) PRICE_EUR = EnumChoices(**get_choices_kwargs(price_mapping_eur)) TYPES = EnumChoices( EXPO=(1, _(u'Выставки')), CONF=(2, _(_(u'Конференции'))), ) class ExtraWhere(object): def __init__(self, sqls, params, operator=AND): self.sqls = sqls self.params = params self.operator = operator def as_sql(self, qn=None, connection=None): sqls = ["(%s)" % sql for sql in self.sqls] operator = " {operator} ".format(operator=self.operator) return operator.join(sqls), tuple(self.params or ()) class GetCourse(object): """GetCourse().convert(currency_id, amount) http://www.cbr.ru/scripts/Root.asp """ cache_timeout = timedelta(hours=24).total_seconds cache_key = getattr(settings, 'COURSE_CACHE_KEY', 'course_data_{0}') target_char_code = 'RUB' def __init__(self, nocache=False, *args, **kwargs): self.nocache = True # self.url = kwargs.get('url', 'http://www.cbr.ru/scripts/XML_daily.asp') self.url = kwargs.get('url', 'http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx') self.alllowed_codes = settings.CURRENCY self.get_data() def build_request_body(self): top = ET.Element('soap:Envelope') top.attrib.update({ 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:xsd': "http://www.w3.org/2001/XMLSchema", 'xmlns:soap': "http://schemas.xmlsoap.org/soap/envelope/", }) body = ET.SubElement(top, 'soap:Body') operation = ET.SubElement(body, 'GetCursOnDate') operation.attrib.update({ 'xmlns': "http://web.cbr.ru/" }) ondate = ET.SubElement(operation, 'On_date') ondate.text = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() return '\n {xml}'.format(xml=ET.tostring(top)) def make_cache_key(self): return self.cache_key.format(date.today().isoformat()) def get_data(self): courses = cache.get(self.make_cache_key()) if self.nocache: # try: # r = requests.post( # url=self.url, # data=self.build_request_body(), # headers={'Content-Type': 'text/xml; charset=utf-8'}) # if r.status_code == requests.codes.ok: # courses = self.parse_xml(r) # cache.set(self.make_cache_key(), courses, self.cache_timeout) # except: # pass # try: # http://stackoverflow.com/questions/19831566/how-to-get-a-soap-body-by-using-soappy imp = Import('http://www.w3.org/2001/XMLSchema') imp.filter.add('http://web.cbr.ru/') d = ImportDoctor(imp) s = Client("http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx?wsdl", doctor=d) result = s.service.GetCursOnDate(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).isoformat()) courses = {} for valute in result.diffgram.ValuteData.ValuteCursOnDate: char_code = valute.VchCode if char_code not in self.alllowed_codes: continue nominal = float(valute.Vnom) value = float(valute.Vcurs) courses[char_code] = round(value / nominal, 3) # except: # pass self.courses = courses def parse_xml(self, response): root = ET.fromstring(response.text.encode(response.encoding)) courses = {} for valute in root.iterfind('Valute'): char_code = valute.find('CharCode').text if char_code not in self.alllowed_codes: continue nominal = float(valute.find('Nominal').text.replace(',', '.')) value = float(valute.find('Value').text.replace(',', '.')) courses[char_code] = round(value / nominal, 3) return courses @property def can_convert(self): return self.courses is not None def convert(self, char_code, amount): '''Returns converted amount and bool indicating convert state ''' if char_code == self.target_char_code: return amount, True if self.can_convert and char_code in self.courses.keys(): return int(round(amount * self.courses[char_code] * 1.03)), True else: return amount, False def convert_or_null(self, char_code, amount): if amount is None: return 0 amount, converted = self.convert(char_code, amount) return amount if amount else 0 def convert_to_eur_or_null(self, amount): '''only from RUB ''' if amount is None: return 0 return int(round(amount / self.courses['EUR']))