Commits

Kostas Papadimitriou committed a3bd349

initial import

Comments (0)

Files changed (7)

+syntax: glob
+.*.swp
+MANIFEST
+build
+dist
+*.pyc
+*.egg-info
+http://web2sms.cosmote.gr Python client
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
+from setuptools import setup, find_packages
+import sys, os
+
+version = '0.2'
+
+setup(name='web2sms',
+      version=version,
+      description="A python client for Cosmote web2sms service.",
+      long_description=open(os.path.join(os.path.dirname(__file__), "README")).read(),
+      classifiers=[
+      "Development Status :: 4 - Beta",
+      "Topic :: Communications :: Telephony",
+      ], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+
+      keywords='sms api cosmote greece mobile client',
+      author='Kostas Papadimitriou',
+      author_email='vinilios@gmail.com',
+      url='http://www.bitbucket.org/vinilios',
+      license="GNU LGPL",
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          "httplib2"
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )

web2sms/__init__.py

+#
+import httplib2
+import re
+
+from xml.dom import minidom as dom
+from BeautifulSoup import BeautifulStoneSoup as BS
+
+first_cap_re = re.compile('(.)([A-Z][a-z]+)')
+all_cap_re = re.compile('([a-z0-9])([A-Z])')
+def uncamel(name):
+    """
+    Converts CamelCase string to non camel case (using underscore to seperate words)
+
+    http://stackoverflow.com/questions/1175208/does-the-python-standard-library-have-function-to-convert-camelcase-to-camel-case/1176023#1176023
+    """
+
+    s1 = first_cap_re.sub(r'\1_\2', name)
+    return all_cap_re.sub(r'\1_\2', s1).lower()
+
+
+def login_required(method):
+    """
+    Login class method decorator
+    """
+    def wrapper(self, *args, **kwargs):
+        if not self.logged_id():
+            self.login()
+
+        return method(self, *args, **kwargs)
+
+    return wrapper
+
+
+class Web2SMSException(Exception):
+    pass
+
+class Web2SMSClient(object):
+    """
+    Client represents the api wrapper class.
+    """
+
+    api_url = "http://web2sms.cosmote.gr/web2sms/DCM"
+
+    def __init__(self, username, password, account, language='EN'):
+        self.username = username
+        self.password = password
+        self.account = account
+        self.language = language
+
+        # Session params
+        self.session_id = None
+        self.user_id = None
+        self.cookie = None
+
+        # http client
+        self.client = None
+
+        # Client status params
+        self.last_status = None
+        self.last_error_message = None
+        self.last_error_code = None
+
+        self.init_client()
+
+    def logged_id(self):
+        return self.session_id and self.user_id
+
+    def login(self):
+        self.make_request("POSTLogin", {'U':self.username, 'P':self.password, 'A': 'WEB2SMS', 'ACCOUNT': self.account, 'L': self.language})
+
+    def make_request(self, action, data, method="POST"):
+        """
+        Prepare xml, session
+        """
+        headers = {'content-type':'application/xml'}
+
+        if self.cookie:
+            # set the cookie to the last response set-cookie value
+            headers.update({'Cookie':self.cookie})
+
+        if self.session_id:
+            data.update({'SID': self.session_id})
+
+        xml = self.get_request_xml(action, data)
+
+        #TODO: handle GET parameters
+        url = self.api_url
+
+        # the actual http request
+        resp, content = self.make_http_request(url, method, xml, headers)
+
+        doc = self.parse_response(resp, content)
+
+        # dynamic method execution to handle special actions (like POSTLogin)
+        custom_parse = getattr(self, "parse_%s" % action, None)
+        if custom_parse:
+            return custom_parse(resp, doc)
+
+        return doc
+
+    def make_http_request(self, url, method, body, headers):
+        resp, content = self.client.request(url, method, body=body, headers=headers)
+        return resp, content
+
+    def parse_response_for_errors(self, doc):
+        self.last_error_message = None
+        self.last_error_code = None
+
+        status = doc.find("status")
+        self.last_status = status.text
+
+        if self.last_status == "0":
+            self.last_error_message = doc.find("error").find("message").text.encode("utf-8")
+            self.last_error_code = doc.find("error").find("code").text.encode("utf-8")
+            raise Exception("[%s] %s" % (self.last_error_code, self.last_error_message))
+
+    def parse_POSTLogin(self, resp_headers, doc):
+        self.user_id = doc.find("session").find("user_id").text
+        self.session_id = doc.find("session").find("sid").text
+        self.cookie = resp_headers['set-cookie']
+
+    def parse_response(self, resp_headers, content):
+        doc = BS(content, fromEncoding="utf-8")
+        self.parse_response_for_errors(doc)
+        return doc
+
+    def init_client(self):
+        self.client = httplib2.Http()
+
+    @login_required
+    def send_sms(self, text, mobile_list, get_status=True, validity=2, date=None):
+        data = {}
+        data['RequestStatusReport'] = "False"
+        if get_status:
+            data['RequestStatusReport'] = "True"
+
+        data['Validity'] = str(validity)
+        data['SendToAll'] = "False"
+        data['SMS'] = str(text)
+
+        if date:
+            raise NotImplemented("Scheduled date is not supported yet.")
+
+        data['UserList'] = {}
+        data['GroupList'] = {}
+        data['MobileList'] = dict([('MOBILE_ID', m) for m in mobile_list])
+
+        self.make_request("SendWEB2SMS", data)
+
+    @login_required
+    def get_status(self):
+        raise NotImplemented("Delivery status is not implemented yet")
+
+    @login_required
+    def delete_sms(self):
+        raise NotImplemented("SMS deletion is not supported")
+
+    def get_request_xml(self, method, params_dict):
+        """
+        Create a valid xml response
+
+        :method parameter: refers to the api action to be executed (e.g. POSTLogin)
+        :params_dict: refers to the action parameters
+        """
+        doc = dom.Document()
+        request = doc.createElement("REQUEST")
+        action = doc.createElement("ACTION")
+        params = doc.createElement("PARAMETERS")
+
+        action.setAttribute("Name", method)
+        for param_name, param_value in list(params_dict.items()):
+
+            param = doc.createElement("PARAM")
+            param.setAttribute("Name", param_name)
+
+            param_type = None
+            if issubclass(type(param_value), basestring):
+                param_type = "String"
+                param.setAttribute("Value", param_value)
+
+            if type(param_value) in [dict]:
+                param_type = "XML"
+                params_list = doc.createElement(uncamel(param_name).upper())
+                for param_item_name, param_item_value in list(param_value.items()):
+                    item = doc.createElement(param_item_name.upper().replace(" ","_"))
+                    data = doc.createTextNode(param_item_value)
+                    item.appendChild(data)
+
+                    params_list.appendChild(item)
+                param.appendChild(params_list)
+            param.setAttribute("Type", param_type)
+            params.appendChild(param)
+
+        request.appendChild(action)
+        request.appendChild(params)
+        doc.appendChild(request)
+        return doc.toxml(encoding="utf-8")
+from unittest import TestCase
+import os
+
+DEMO_USER = os.env.get("WEB2SMS_DEMO_USERNAME")
+DEMO_PASSWORD = os.env.get("WEB2SMS_DEMO_PASSWORD")
+DEMO_ACCOUNT = os.env.get("WEB2SMS_DEMO_ACCOUNT")
+DEMO_MOBILE = os.env.get("WEB2SMS_DEMO_MOBILE")
+
+class Web2smsTests(TestCase):
+
+    def setUp(self):
+        pass
+
+    def _get_client(self, *params, **kwargs):
+        from web2sms.api import Client
+        return Client(*params, **kwargs)
+
+    def ntest_client(self):
+        c = self._get_client("user","pass","account","en")
+        complex_request = c.get_request_xml("SendWEB2SMS",
+                {'SID':'SessionId', 'SendToAll': "False",
+                    'UserList':{}, 'MobileList':
+                            {'Mobile_id':"694111111",'Mobile_id':"695111111"}
+                })
+
+        complex_request_out = """
+        <?xml version="1.0" encoding="utf-8" ?>
+        <REQUEST><ACTION Name="SendWEB2SMS" /><PARAMETERS>
+        <PARAM Name="MobileList" Type="XML">
+        <MOBILE_LIST><MOBILE_ID>695111111</MOBILE_ID></MOBILE_LIST></PARAM>
+        <PARAM Name="UserList" Type="XML"><USER_LIST/></PARAM>
+        <PARAM Name="SendToAll" Type="String" Value="False"/>
+        <PARAM Name="SID" Type="String" Value="SessionId"/>
+        </PARAMETERS>
+        </REQUEST>
+        """
+        # clear spaces making easier to compare
+        self.assertEqual(complex_request.replace(" ",""),complex_request_out.replace("\n","").replace(" ",""))
+
+    def test_send_sms(self):
+        c = self._get_client(DEMO_USER,DEMO_PASSWORD,DEMO_ACCOUNT,"EN")
+        c.send_sms("test sms", [DEMO_MOBILE])
+