You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

184 lines
7.6 KiB

# -*- 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 '<?xml version="1.0" encoding="utf-8"?>\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']))