def test_enum_key_should_have_right_format(self): with assert_raises(ValueError): Enum(1, 2) with assert_raises(ValueError): Enum('1A', 'B') with assert_raises(ValueError): Enum('A-B', 'B') Enum('A_B_3', 'B')
def test_enum_should_contain_only_defined_values(self): enum = Enum('A', 'B') assert_equal(enum.A, 'A') assert_equal(enum.B, 'B') assert_equal(list(enum), ['A', 'B']) assert_equal(enum.all, ('A', 'B')) assert_equal(enum.get_name('A'), 'A') with assert_raises(AttributeError): enum.C # pylint: disable=W0104 assert_is_none(enum.get_name('C')) assert_in('A', enum) assert_in(enum.A, enum) assert_not_in('C', enum)
def test_enum_with_distinct_key_and_value_should_contain_only_defined_values( self): enum = Enum(('A', 'c'), ('B', 'd')) assert_equal(enum.A, 'c') assert_equal(enum.B, 'd') assert_equal(list(enum), ['c', 'd']) assert_equal(enum.all, ('c', 'd')) assert_equal(enum.get_name('c'), 'A') with assert_raises(AttributeError): enum.C # pylint: disable=W0104 assert_is_none(enum.get_name('f')) assert_in('c', enum) assert_in(enum.A, enum) assert_not_in('A', enum)
def test_enum_should_contain_only_defined_values(self): enum = Enum( 'A', 'B' ) assert_equal(enum.A, 'A') assert_equal(enum.B, 'B') assert_equal(list(enum), ['A', 'B']) assert_equal(enum.all, ('A', 'B')) assert_equal(enum.get_name('A'), 'A') with assert_raises(AttributeError): enum.C # pylint: disable=W0104 assert_is_none(enum.get_name('C')) assert_in('A', enum) assert_in(enum.A, enum) assert_not_in('C', enum)
def test_enum_with_distinct_key_and_value_should_contain_only_defined_values(self): enum = Enum( ('A', 'c'), ('B', 'd') ) assert_equal(enum.A, 'c') assert_equal(enum.B, 'd') assert_equal(list(enum), ['c', 'd']) assert_equal(enum.all, ('c', 'd')) assert_equal(enum.get_name('c'), 'A') with assert_raises(AttributeError): enum.C # pylint: disable=W0104 assert_is_none(enum.get_name('f')) assert_in('c', enum) assert_in(enum.A, enum) assert_not_in('A', enum)
class Serializer: """ REST serializer and deserializer, firstly is data serialized to standard python data types and after that is used convertor for final serialization """ SERIALIZATION_TYPES = Enum('VERBOSE', 'RAW', 'BOTH') def __init__(self, request=None): self.request = request def _get_serializer(self, data): return get_serializer(data, request=self.request) def _data_to_python(self, data, serialization_format, lazy=False, **kwargs): return self._get_serializer(data).serialize(data, serialization_format, **kwargs) def _lazy_data_to_python(self, data, serialization_format, lazy=False, **kwargs): if lazy: return LazySerializedData( self._get_serializer(data), data, serialization_format, lazy=lazy, **kwargs ) else: return self._data_to_python(data, serialization_format, lazy=lazy, **kwargs) def serialize(self, data, serialization_format, **kwargs): raise NotImplementedError def deserialize(self, data): raise NotImplementedError
class Serializer(object): """ REST serializer and deserializer, firstly is data serialized to standard python data types and after that is used convertor for final serialization """ SERIALIZATION_TYPES = Enum('VERBOSE', 'RAW', 'BOTH') def _get_resource(self, request, obj): from .resource import typemapper resource_class = typemapper.get(type(obj)) if resource_class: return resource_class(request) def _to_python_via_resource(self, request, thing, serialization_format, **kwargs): resource = self._get_resource(request, thing) if resource: thing._resource = resource return resource.serializer(resource)._to_python( request, thing, serialization_format, **kwargs) else: return None def _find_to_serializer(self, thing): for serializer in value_serializers: if serializer._can_transform_to_python(thing): return serializer def _to_python_chain(self, request, thing, serialization_format, **kwargs): if not hasattr(thing, '_resource'): result = self._to_python_via_resource(request, thing, serialization_format, **kwargs) if result: return result serializer = self._find_to_serializer(thing) if serializer: return serializer._to_python(request, thing, serialization_format, **kwargs) raise NotImplementedError('Serializer not found for %s' % thing) def _to_python(self, request, thing, serialization_format, **kwargs): return self._to_python_chain(request, thing, serialization_format, **kwargs) def _can_transform_to_python(self, thing): raise NotImplementedError
from chamber.utils.datastructures import Enum DIRECTION = Enum( 'ASC', 'DESC', )
from collections import OrderedDict import re from chamber.utils.datastructures import Enum from django.apps import apps from django.conf import settings as django_settings from django.utils.module_loading import import_string from attrdict import AttrDict DEFAULT_SENDER_BACKEND_NAME = 'default' CONTROLLER_TYPES = Enum( 'SMS', 'EMAIL', 'DIALER', 'PUSH_NOTIFICATION', ) DEFAULTS = { # SMS configuration 'SMS_BACKENDS': { DEFAULT_SENDER_BACKEND_NAME: { 'backend': 'pymess.backend.sms.dummy.DummySMSBackend' } }, 'SMS_DEFAULT_SENDER_BACKEND_NAME': DEFAULT_SENDER_BACKEND_NAME, 'SMS_BACKEND_ROUTER': 'pymess.backend.routers.DefaultBackendRouter', 'SMS_TEMPLATE_MODEL': 'pymess.SMSTemplate', 'SMS_USE_ACCENT': False, 'SMS_DEFAULT_PHONE_CODE': None,
class ATSSMSBackend(SMSBackend): """ SMS backend that implements ATS operator service https://www.atspraha.cz/ Backend supports check SMS delivery """ REQUEST_TYPES = Enum( 'SMS', 'DELIVERY_REQUEST', ) TEMPLATES = { 'base': 'pymess/sms/ats/base.xml', REQUEST_TYPES.SMS: 'pymess/sms/ats/sms.xml', REQUEST_TYPES.DELIVERY_REQUEST: 'pymess/sms/ats/delivery_request.xml', } class ATSSendingError(Exception): pass ATS_STATES = ChoicesNumEnum( # SMS delivery receipts ('NOT_FOUND', _('not found'), 20), ('NOT_SENT', _('not sent yet'), 21), ('SENT', _('sent'), 22), ('DELIVERED', _('delivered'), 23), ('NOT_DELIVERED', _('not delivered'), 24), ('UNKNOWN', _('not able to determine the state'), 25), # Authentication ('AUTHENTICATION_FAILED', _('authentication failed'), 100), # Internal errors ('DB_ERROR', _('DB error'), 200), # Request states ('OK', _('SMS is OK and ready to be sent'), 0), ('UNSPECIFIED_ERROR', _('unspecified error'), 1), ('BATCH_WITH_NOT_UNIQUE_UNIQ', _('one of the requests has not unique "uniq"'), 300), ('SMS_NOT_UNIQUE_UNIQ', _('SMS has not unique "uniq"'), 310), ('SMS_NO_KW', _('SMS lacks keyword'), 320), ('KW_INVALID', _('keyword not valid'), 321), ('NO_SENDER', _('no sender specified'), 330), ('SENDER_INVALID', _('sender not valid'), 331), ('MO_PR_NOT_ALLOWED', _('MO PR SMS not allowed'), 332), ('MT_PR_NOT_ALLOWED', _('MT PR SMS not allowed'), 333), ('MT_PR_DAILY_LIMIT', _('MT PR SMS daily limit exceeded'), 334), ('MT_PR_TOTAL_LIMIT', _('MT PR SMS total limit exceeded'), 335), ('GEOGRAPHIC_NOT_ALLOWED', _('geographic number is not allowed'), 336), ('MT_SK_NOT_ALLOWED', _('MT SMS to Slovakia not allowed'), 337), ('SHORTCODES_NOT_ALLOWED', _('shortcodes not allowed'), 338), ('UNKNOWN_SENDER', _('sender is unknown'), 339), ('UNSPECIFIED_SMS_TYPE', _('type of SMS not specified'), 340), ('TOO_LONG', _('SMS too long'), 341), ('TOO_MANY_PARTS', _('too many SMS parts (max. is 10)'), 342), ('WRONG_SENDER_OR_RECEIVER', _('wrong number of sender/receiver'), 343), ('NO_RECIPIENT_OR_WRONG_FORMAT', _('recipient is missing or in wrong format'), 350), ('TEXTID_NOT_ALLOWED', _('using "textid" is not allowed'), 360), ('WRONG_TEXTID', _('"textid" is in wrong format'), 361), ('LONG_SMS_TEXTID_NOT_ALLOWED', _('long SMS with "textid" not allowed'), 362), # XML errors ('XML_MISSING', _('XML body missing'), 701), ('XML_UNREADABLE', _('XML is not readable'), 702), ('WRONG_HTTP_METHOD', _('unknown HTTP method or not HTTP POST'), 703), ('XML_INVALID', _('XML invalid'), 705), ) ATS_STATES_MAPPING = { ATS_STATES.NOT_FOUND: OutputSMSMessage.STATE.ERROR, ATS_STATES.NOT_SENT: OutputSMSMessage.STATE.SENDING, ATS_STATES.SENT: OutputSMSMessage.STATE.SENT, ATS_STATES.DELIVERED: OutputSMSMessage.STATE.DELIVERED, ATS_STATES.NOT_DELIVERED: OutputSMSMessage.STATE.ERROR, ATS_STATES.OK: OutputSMSMessage.STATE.SENDING, ATS_STATES.UNSPECIFIED_ERROR: OutputSMSMessage.STATE.ERROR, ATS_STATES.BATCH_WITH_NOT_UNIQUE_UNIQ: OutputSMSMessage.STATE.ERROR, ATS_STATES.SMS_NOT_UNIQUE_UNIQ: OutputSMSMessage.STATE.ERROR, ATS_STATES.SMS_NO_KW: OutputSMSMessage.STATE.ERROR, ATS_STATES.KW_INVALID: OutputSMSMessage.STATE.ERROR, ATS_STATES.NO_SENDER: OutputSMSMessage.STATE.ERROR, ATS_STATES.SENDER_INVALID: OutputSMSMessage.STATE.ERROR, ATS_STATES.MO_PR_NOT_ALLOWED: OutputSMSMessage.STATE.ERROR, ATS_STATES.MT_SK_NOT_ALLOWED: OutputSMSMessage.STATE.ERROR, ATS_STATES.SHORTCODES_NOT_ALLOWED: OutputSMSMessage.STATE.ERROR, ATS_STATES.UNKNOWN_SENDER: OutputSMSMessage.STATE.ERROR, ATS_STATES.UNSPECIFIED_SMS_TYPE: OutputSMSMessage.STATE.ERROR, ATS_STATES.TOO_LONG: OutputSMSMessage.STATE.ERROR, ATS_STATES.TOO_MANY_PARTS: OutputSMSMessage.STATE.ERROR, ATS_STATES.WRONG_SENDER_OR_RECEIVER: OutputSMSMessage.STATE.ERROR, ATS_STATES.NO_RECIPIENT_OR_WRONG_FORMAT: OutputSMSMessage.STATE.ERROR, ATS_STATES.TEXTID_NOT_ALLOWED: OutputSMSMessage.STATE.ERROR, ATS_STATES.WRONG_TEXTID: OutputSMSMessage.STATE.ERROR, ATS_STATES.LONG_SMS_TEXTID_NOT_ALLOWED: OutputSMSMessage.STATE.ERROR, } config = AttrDict({ 'UNIQ_PREFIX': '', 'VALIDITY': 60, 'TEXTID': None, 'URL': 'http://fik.atspraha.cz/gwfcgi/XMLServerWrapper.fcgi', 'OPTID': '', 'TIMEOUT': 5, # 5s }) def _get_extra_sender_data(self): return { 'prefix': self.config.UNIQ_PREFIX, 'validity': self.config.VALIDITY, 'kw': self.config.PROJECT_KEYWORD, 'textid': self.config.TEXTID, } def get_extra_message_kwargs(self): return { 'sender': self.config.OUTPUT_SENDER_NUMBER, } def _serialize_messages(self, messages, request_type): """ Serialize SMS messages to the XML :param messages: list of SMS messages :param request_type: type of the request to the ATS operator :return: serialized XML message that will be sent to the ATS service """ return render_to_string( self.TEMPLATES['base'], { 'username': self.config.USERNAME, 'password': self.config.PASSWORD, 'template_type': self.TEMPLATES[request_type], 'messages': messages, 'prefix': str(self.config.UNIQ_PREFIX) + '-', 'sender': self.config.OUTPUT_SENDER_NUMBER, 'dlr': 1, 'validity': self.config.VALIDITY, 'kw': self.config.PROJECT_KEYWORD, 'billing': 0, 'extra': mark_safe(' textid="{textid}"'.format( textid=self.config.TEXTID)) if self.config.TEXTID else '', }) def _send_requests(self, messages, request_type, is_sending=False, **change_sms_kwargs): """ Performs the actual POST request for input messages and request type. :param messages: list of SMS messages :param request_type: type of the request :param is_sending: True if method is called after sending message :param change_sms_kwargs: extra kwargs that will be stored to the message object """ requests_xml = self._serialize_messages(messages, request_type) try: resp = generate_session(slug='pymess - ATS SMS', related_objects=list(messages)).post( self.config.URL, data=requests_xml, headers={'Content-Type': 'text/xml'}, timeout=self.config.TIMEOUT) if resp.status_code != 200: raise self.ATSSendingError( 'ATS operator returned invalid response status code: {}'. format(resp.status_code)) self._update_sms_states_from_response( messages, self._parse_response_codes(resp.text), is_sending, **change_sms_kwargs) except requests.exceptions.RequestException as ex: raise self.ATSSendingError( 'ATS operator returned returned exception: {}'.format(str(ex))) def _update_sms_states_from_response(self, messages, parsed_response, is_sending=False, **change_sms_kwargs): """ Higher-level function performing serialization of ATS requests, parsing ATS server response and updating SMS messages state according the received response. :param messages: list of SMS messages :param parsed_response: parsed HTTP response from the ATS service :param is_sending: True if update is called after sending message :param change_sms_kwargs: extra kwargs that will be stored to the message object """ messages_dict = {message.pk: message for message in messages} missing_uniq = set(messages_dict.keys()) - set(parsed_response.keys()) if missing_uniq: raise self.ATSSendingError( 'ATS operator not returned SMS info with uniq: {}'.format( ', '.join(map(str, missing_uniq)))) extra_uniq = set(parsed_response.keys()) - set(messages_dict.keys()) if extra_uniq: raise self.ATSSendingError( 'ATS operator returned SMS info about unknown uniq: {}'.format( ', '.join(map(str, extra_uniq)))) for uniq, ats_state in parsed_response.items(): sms = messages_dict[uniq] state = self.ATS_STATES_MAPPING.get(ats_state) error = self.ATS_STATES.get_label( ats_state) if state == OutputSMSMessage.STATE.ERROR else None if is_sending: if error: self._update_message_after_sending_error( sms, state=state, error=error, extra_sender_data={'sender_state': ats_state}, **change_sms_kwargs) else: self._update_message_after_sending( sms, state=state, extra_sender_data={'sender_state': ats_state}, **change_sms_kwargs) else: self._update_message( sms, state=state, error=error, extra_sender_data={'sender_state': ats_state}, **change_sms_kwargs) def publish_messages(self, messages): self._send_requests(messages, request_type=self.REQUEST_TYPES.SMS, is_sending=True, sent_at=timezone.now()) def publish_message(self, message): try: self._send_requests([message], request_type=self.REQUEST_TYPES.SMS, is_sending=True, sent_at=timezone.now()) except self.ATSSendingError as ex: self._update_message_after_sending_error( message, state=OutputSMSMessage.STATE.ERROR, error=str(ex), ) except requests.exceptions.RequestException as ex: # Service is probably unavailable sending will be retried self._update_message_after_sending_error(message, error=str(ex)) # Do not re-raise caught exception. Re-raise exception causes transaction rollback (lost of information # about exception). def _parse_response_codes(self, xml): """ Finds all <code> tags in the given XML and returns a mapping "uniq" -> "response code" for all SMS. In case of an error, the error is logged. :param xml: XML from the ATL response :return: dictionary with pair {SMS uniq: response status code} """ soup = BeautifulSoup(xml, 'html.parser') code_tags = soup.find_all('code') error_message = ', '.join([ (str(self.ATS_STATES.get_label(c)) if c in self.ATS_STATES.all else 'ATS returned an unknown state {}.'.format(c)) for c in [ int(error_code.string) for error_code in code_tags if not error_code.attrs.get('uniq') ] ], ) if error_message: raise self.ATSSendingError( 'Error returned from ATS operator: {}'.format(error_message)) return { int(code.attrs['uniq'].lstrip(str(self.config.UNIQ_PREFIX) + '-')): int(code.string) for code in code_tags if code.attrs.get('uniq') } def update_sms_states(self, messages): self._send_requests(messages, request_type=self.REQUEST_TYPES.DELIVERY_REQUEST)
from pyston.utils import LOOKUP_SEP from .exceptions import FilterValueError, OperatorFilterError OPERATORS = Enum( ('GT', 'gt'), ('LT', 'lt'), ('EQ', 'eq'), ('NEQ', 'neq'), ('LTE', 'lte'), ('GTE', 'gte'), ('CONTAINS', 'contains'), ('ICONTAINS', 'icontains'), ('RANGE', 'range'), ('EXACT', 'exact'), ('IEXACT', 'iexact'), ('STARTSWITH', 'startswith'), ('ISTARTSWITH', 'istartswith'), ('ENDSWITH', 'endswith'), ('IENDSWITH', 'iendswith'), ('IN', 'in'), ('RANGE', 'range'), ('ALL', 'all'), ('ISNULL', 'isnull'), ) NONE_LABEL = _('(None)') class Operator:
class SMSOperatorBackend(SMSBackend): """ SMS backend that implements ATS operator service https://www.sms-operator.cz/ Backend supports check SMS delivery """ class SMSOperatorSendingError(Exception): pass REQUEST_TYPES = Enum( 'SMS', 'DELIVERY_REQUEST', ) TEMPLATES = { 'base': 'pymess/sms/sms_operator/base.xml', REQUEST_TYPES.SMS: 'pymess/sms/sms_operator/sms.xml', REQUEST_TYPES.DELIVERY_REQUEST: 'pymess/sms/sms_operator/delivery_request.xml', } SMS_OPERATOR_STATES = ChoicesNumEnum( # SMS states ('DELIVERED', _('delivered'), 0), ('NOT_DELIVERED', _('not delivered'), 1), ('PHONE_NUMBER_NOT_EXISTS', _('number not exists'), 2), # SMS not moved to GSM operator ('TIMEOUTED', _('timeouted'), 3), ('INVALID_PHONE_NUMBER', _('wrong number format'), 4), ('ANOTHER_ERROR', _('another error'), 5), ('EVENT_ERROR', _('event error'), 6), ('SMS_TEXT_TOO_LONG', _('SMS text too long'), 7), # SMS with more parts ('PARTLY_DELIVERED', _('partly delivered'), 10), ('UNKNOWN', _('unknown'), 11), ('PARLY_DELIVERED_PARTLY_UNKNOWN', _('partly delivered, partly unknown'), 12), ('PARTLY_NOT_DELIVERED_PARTLY_UNKNOWN', _('partly not delivered, partly unknown'), 13), ('PARTLY_DELIVERED_PARTLY_NOT_DELIVERED_PARTLY_UNKNOWN', _('partly delivered, partly not delivered, partly unknown'), 14), ('NOT_FOUND', _('not found'), 15), ) SMS_OPERATOR_STATES_MAPPING = { SMS_OPERATOR_STATES.DELIVERED: OutputSMSMessage.STATE.DELIVERED, SMS_OPERATOR_STATES.NOT_DELIVERED: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.PHONE_NUMBER_NOT_EXISTS: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.TIMEOUTED: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.INVALID_PHONE_NUMBER: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.ANOTHER_ERROR: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.EVENT_ERROR: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.SMS_TEXT_TOO_LONG: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.PARTLY_DELIVERED: OutputSMSMessage.STATE.ERROR_UPDATE, SMS_OPERATOR_STATES.UNKNOWN: OutputSMSMessage.STATE.SENDING, SMS_OPERATOR_STATES.PARLY_DELIVERED_PARTLY_UNKNOWN: OutputSMSMessage.STATE.SENDING, SMS_OPERATOR_STATES.PARTLY_NOT_DELIVERED_PARTLY_UNKNOWN: OutputSMSMessage.STATE.SENDING, SMS_OPERATOR_STATES.PARTLY_DELIVERED_PARTLY_NOT_DELIVERED_PARTLY_UNKNOWN: OutputSMSMessage.STATE.SENDING, SMS_OPERATOR_STATES.NOT_FOUND: OutputSMSMessage.STATE.ERROR_UPDATE, } config = AttrDict({ 'URL': 'https://www.sms-operator.cz/webservices/webservice.aspx', 'UNIQ_PREFIX': '', 'TIMEOUT': 5, # 5s }) def _get_extra_sender_data(self): return { 'prefix': self.config.UNIQ_PREFIX, } def _serialize_messages(self, messages, request_type): """ Serialize SMS messages to the XML :param messages: list of SMS messages :param request_type: type of the request to the SMS operator :return: serialized XML message that will be sent to the SMS operator service """ return render_to_string( self.TEMPLATES['base'], { 'username': self.config.USERNAME, 'password': self.config.PASSWORD, 'prefix': str(self.config.UNIQ_PREFIX) + '-', 'template_type': self.TEMPLATES[request_type], 'messages': messages, 'type': 'SMS' if request_type == self.REQUEST_TYPES.SMS else 'SMS-Status', }) def _send_requests(self, messages, request_type, is_sending=False, **change_sms_kwargs): """ Performs the actual POST request for input messages and request type. :param messages: list of SMS messages :param request_type: type of the request :param is_sending: True if method is called after sending message :param change_sms_kwargs: extra kwargs that will be stored to the message object """ requests_xml = self._serialize_messages(messages, request_type) try: resp = generate_session(slug='pymess - SMS operator', related_objects=list(messages)).post( self.config.URL, data=requests_xml, headers={'Content-Type': 'text/xml'}, timeout=self.config.TIMEOUT) if resp.status_code != 200: raise self.SMSOperatorSendingError( 'SMS operator returned invalid response status code: {}'. format(resp.status_code)) self._update_sms_states_from_response( messages, self._parse_response_codes(resp.text), is_sending, **change_sms_kwargs) except requests.exceptions.RequestException as ex: raise self.SMSOperatorSendingError( 'SMS operator returned returned exception: {}'.format(str(ex))) def _update_sms_states_from_response(self, messages, parsed_response, is_sending=False, **change_sms_kwargs): """ Higher-level function performing serialization of SMS operator requests, parsing ATS server response and updating SMS messages state according the received response. :param messages: list of SMS messages :param parsed_response: parsed HTTP response from the SMS operator service :param is_sending: True if update is called after sending message :param change_sms_kwargs: extra kwargs that will be stored to the message object """ messages_dict = {message.pk: message for message in messages} missing_uniq = set(messages_dict.keys()) - set(parsed_response.keys()) if missing_uniq: raise self.SMSOperatorSendingError( 'SMS operator not returned SMS info with uniq: {}'.format( ', '.join(map(str, missing_uniq)))) extra_uniq = set(parsed_response.keys()) - set(messages_dict.keys()) if extra_uniq: raise self.SMSOperatorSendingError( 'SMS operator returned SMS info about unknown uniq: {}'.format( ', '.join(map(str, extra_uniq)))) for uniq, sms_operator_state in parsed_response.items(): sms = messages_dict[uniq] state = self.SMS_OPERATOR_STATES_MAPPING.get(sms_operator_state) error = (self.SMS_OPERATOR_STATES.get_label(sms_operator_state) if state == OutputSMSMessage.STATE.ERROR_UPDATE else None) if is_sending: if error: self._update_message_after_sending_error( sms, state=state, error=error, extra_sender_data={'sender_state': sms_operator_state}, **change_sms_kwargs) else: self._update_message_after_sending( sms, state=state, extra_sender_data={'sender_state': sms_operator_state}, **change_sms_kwargs) else: self._update_message( sms, state=state, error=error, extra_sender_data={'sender_state': sms_operator_state}, **change_sms_kwargs) def publish_message(self, message): try: self._send_requests([message], request_type=self.REQUEST_TYPES.SMS, is_sending=True, sent_at=timezone.now()) except self.SMSOperatorSendingError as ex: self._update_message_after_sending_error( message, state=OutputSMSMessage.STATE.ERROR, error=str(ex)) except requests.exceptions.RequestException as ex: self._update_message_after_sending_error(message, error=str(ex)) # Do not re-raise caught exception. Re-raise exception causes transaction rollback (lost of information # about exception). def publish_messages(self, messages): self._send_requests(messages, request_type=self.REQUEST_TYPES.SMS, is_sending=True, sent_at=timezone.now()) def _parse_response_codes(self, xml): """ Finds all <dataitem> tags in the given XML and returns a mapping "uniq" -> "response code" for all SMS. In case of an error, the error is logged. :param xml: XML from the SMS operator response :return: dictionary with pair {SMS uniq: response status code} """ soup = BeautifulSoup(xml, 'html.parser') return { int(item.smsid.string.lstrip(self.config.UNIQ_PREFIX + '-')): int(item.status.string) for item in soup.find_all('dataitem') } def update_sms_states(self, messages): self._send_requests(messages, request_type=self.REQUEST_TYPES.DELIVERY_REQUEST)
from __future__ import unicode_literals from chamber.utils.datastructures import Enum LOGICAL_OPERATORS = Enum( 'AND', 'OR', 'NOT', )