<file_sep># Unoffical AdminTool2 API!
This is a unoffical API for Tele2's customercare tool AdminTool2
This API has been created to make easy automatic interactions with the AdminTool2 WEB GUI

WORK IN PROGRESS!!!!
<file_sep>#!env/bin/python
# -*- coding: utf-8 -*-
"""
Made By <NAME>!
Unofficial API for Tele2's customerservice tool AdminTool2
All functions return a dictionary where status = statuscode
"""
import urllib, urllib2, re, string
from bs4 import BeautifulSoup
from argparse import ArgumentParser
from getpass import getpass
from sys import exit

class SessionHandler():
    def __init__(self, username, password, baseurl='https://admintool2.tele2.se'):
        self.username = username
        self.password = <PASSWORD>
        self.baseurl = baseurl
        self.sessionstate = False

    def login(self):
        """
        Login function used for establishing first connection to Admintool2.
        Stores the
        """
        uri='/AdminTool2/base/j_security_check'
        uriencoding={'j_username':self.username,
                        'j_password':<PASSWORD>,
                        '&__checkbox_chksaveuser':'false',
                        'login.button':'Log+in'
                        }

        opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
        urllib2.install_opener(opener)

        try:
            urllib2.urlopen(self.baseurl + '/AdminTool2/base/')
        except (urllib2.HTTPError), e:
            return {'status':e.code}
        try:
            f = urllib2.urlopen(self.baseurl + uri,
                urllib.urlencode(uriencoding))

            bs = BeautifulSoup(f, 'html.parser')

            if bs.find(id='errdiv') is not None:
                return {'status':503}
            else:
                self.sessionstate = True
                return {'status':200}

        except (urllib2.HTTPError), e:
            return {'status':e.code}

class Access():
    """
    Handles Everything related to Access searches, needs a valid SessionHandler at __init__
    """
    def __init__(self, SessionHandler):
        self.SessionHandler = SessionHandler

    def find_by_msisdn(self, msisdn):
        """
        Searches for a access, returns the customer as a dictionary, or error message
        Login before calling this!
        """
        #Check if logged in
        if not self.SessionHandler.sessionstate:
            return {'status':503}

        uri='/AdminTool2/base/Access.search.action'
        uriencoding={'searchAccessName':msisdn,
                        'searchUserName':'',
                        'searchAccessType':'all',
                        'button.find':'Find HTTP/1.1'}

        try:
            f = urllib2.urlopen(self.SessionHandler.baseurl + uri,urllib.urlencode(uriencoding))
            if urllib2.getcode() == 403:
                return {'status':403}
            bs = BeautifulSoup(f, "html.parser")

            if 'Service list' in bs.text: # Only found if there are no access found
                return self.parse_single_access(bs)
            elif bs.find('tr', {'class','odd'}) is not None: # Only found if there are multiple accesses on the same MSISDN
                return self.return_multiple_access(bs)
            else:
                return {'status':404}

        except (urllib2.HTTPError), e:
            return {'status':e.code}

    def parse_single_access(self,bs):
        if 'Service list' in bs.text:
            dataobject = {}
            tables = bs.find_all('table', {'class','table'})
            for count, table in enumerate(tables):

                # Each access has 3 tables, 2 with account information, 1 with service information, This if statement handles the account information!
                if count < 2:
                    for tr in table.find_all('tr', recursive=False):
                        td = tr.find_all('td', recursive=False)
                        if len(td) == 2:
                            key=td[0].text.strip().lower()

                            # Regexp... Making code unreadable since 1980
                            # Multiple substituions separated by |
                            key = re.compile('(\:|\(.*?\)|\ |\.|\n)').sub('', key)
                            data=td[1].text.strip()
                            data = re.compile('(\n|\ +)').sub('',data)

                            #Special case due to reprovision button in the middle of the table
                            if key =='accessname':
                                data = re.sub("\D", "", data)
                            if key != "":
                                dataobject[key]=data
                        # Only handle cases where td[0]==key & td[1]==value
                        else:
                            continue
                # Else statement for when we reach the service list
            else:
                    # If access has no services return empty list
                    if table.find('tr', {'class', 'empty'}) is not None:
                        dataobject['services']=[]
                    else:
                        dataobject['services'] = []
                        tbody = table.find('tbody', recursive=False)
                        trs = tbody.find_all('tr', recursive=False)
                        for tr in trs:
                            tds = tr.find_all('td', recursive=False)

                            # Regexp for removing unwanted characters
                            rep = re.compile('(\r|\n|\t|\ +)')

                            serviceid = re.sub(rep,'', tds[0].text)
                            servicename = re.sub(rep,'', tds[1].text)
                            product = re.sub(rep,'', tds[2].text)
                            servicetype = re.sub(rep,'', tds[3].text)
                            billing = re.sub(rep,'', tds[4].text)
                            provisioning = re.sub(rep,'', tds[5].text)
                            abuse = re.sub(rep,'', tds[6].text)
                            capusername = re.sub(rep,'', tds[7].text)

                            # Appends one new list object for each service
                            dataobject['services'].append({'serviceid':serviceid, 'servicename':servicename,
                                'product':product, 'servicetype':servicetype, 'billing':billing,'provisioning':provisioning,
                                'abuse':abuse,'capusername':capusername})

            dataobject['status']=200
            return dataobject
        else:
            return {'status':404}

    def find_by_access_id(self, accessid):
        """
        This method is called by takes billing id, returns access dictionary or 404 for not found, or 503 for SessionHandler error
        """
        if not self.SessionHandler.sessionstate:
            return {'status':503}
        try:
            uri='/AdminTool2/base/Access.details.action'
            uriencoding={'detailsAccessId':accessid}

            f = urllib2.urlopen(self.SessionHandler.baseurl + uri,urllib.urlencode(uriencoding))
            if f.getcode() == 403:
                return {'status':403}
            bs = BeautifulSoup(f, "html.parser")
            return self.parse_single_access(bs)

        except (urllib2.HTTPError), e:
            return {'status':e.code}

    def return_multiple_access(self, bs):
        """
        In cases where a single access can't be determined, this function returns all access with the billingID
        """
        multipleaccessobject={'access':[], 'status':303}
        trs = bs.find('tr',{'class', 'odd'}).find_parent().find_all('tr')
        reg = re.compile('(\r|\n|\t|\ +)')

        for tr in trs:
            tds = tr.find_all('td')
            accessid = re.sub(reg,'',tds[0].text)
            product = re.sub(reg,'',tds[1].text)
            accesstype = re.sub(reg,'',tds[2].text)
            billing = re.sub(reg,'',tds[3].text)
            provisioning = re.sub(reg,'',tds[4].text)
            abuse = re.sub(reg,'',tds[5].text)
            multipleaccessobject['access'].append({'accessid':accessid, 'product':product, 'accesstype':accesstype, 'billing':billing, 'provisioning':provisioning, 'abuse':abuse})
        return multipleaccessobject

class Customer():
    def __init__(self, SessionHandler):
        self.SessionHandler = SessionHandler

    def parse_single_customer(self, bs):

        reg = re.compile('(\:|\ |\.|\n|\ +|\r|\t)')
        customerObject = {}
        customerObject['accesslist'] = []
        customerObject['accessindependentlist'] = []

        mainTable = bs.find('table', {'class','wwFormTable'})
        billingInput = mainTable.find(id='Customer_capBillingData_id')
        billingID = billingInput['value']

        customerObject['billingID'] = billingID

        trs = mainTable.find_all('tr', recursive=True)
        for tr in trs:
            tds = tr.find_all('td',recursive=True)
            key = re.compile('(\:|\(.*?\)|\ |\.|\n|\ +)').sub('', tds[0].text.lower())
            value = tds[1].text.strip()

            # Removes the spacer columns
            if key != '':
                # A customer can have up to 3 addresses, these are free text fields for our customercare groups, I've never seen them used properly
                # So i'm just ignoring them instead if we already have 1 customer address
                if customerObject.has_key(key) == False:
                    customerObject[key]=value
                elif customerObject[key]=='':
                    customerObject[key]=value

        mainDiv = bs.find('div', {'class','col-md-10'})
        displays = mainDiv.find_all('div', {'class', 'display'})

        accessList = displays[0].find('tbody')
        accessIndependetList = displays[1].find('tbody')

        for trs in accessList.find_all('tr'):
            tds = trs.find_all('td')
            if len(tds) != 1:
                accessName = re.sub(reg, '', tds[1].text)
                product = re.sub(reg,'',tds[2].text)
                accessType = re.sub(reg,'',tds[3].text)
                billing = re.sub(reg,'',tds[4].text)
                provisioning = re.sub(reg,'',tds[5].text)
                abuse = re.sub(reg,'',tds[6].text)

                customerObject['accesslist'].append({'accessName':accessName, 'product':product,
                    'accessType':accessType, 'billing':billing,'provisioning':provisioning,'abuse':abuse})

        for trs in accessIndependetList.find_all('tr'):
            tds = trs.find_all('td')
            if len(tds) != 1:
                serviceName = re.sub(reg, '', tds[1].text)
                product = re.sub(reg, '', tds[2].text)
                servicename = re.sub(reg, '', tds[3].text)
                billing = re.sub(reg, '', tds[4].text)
                provisioning = re.sub(reg, '', tds[5].text)
                abuse = re.sub(reg, '', tds[6].text)
                capusername = re.sub(reg, '', tds[7].text)

                customerObject['accessindependentlist'].append({'serviceName':serviceName, 'product':product, 'servicename':servicename,
                    'billing':billing, 'provisioning':provisioning, 'abuse':abuse, 'capusername':capusername})

        customerObject['status']=200
        return customerObject

    def find_by_customer_id(self, customerid):
        uri = '/AdminTool2/base/Customer.search.action'
        uriencoding = {'submitted':'true',
                        'formAdvanced':'false',
                        'ownerId':customerid,
                        'ssn':'',
                        'surname':'',
                        'address1':'',
                        'company':'',
                        'searchLegalentity':'',
                        'button.find':'Find HTTP/1.1'}

        try:
            # Check if user is logged in
            if not self.SessionHandler.sessionstate:
                return {'status':503}

            f = urllib2.urlopen(self.SessionHandler.baseurl + uri, urllib.urlencode(uriencoding))
            if f.getcode() == 403:
                return {'status':403}
            bs = BeautifulSoup(f, "html.parser")

            # class empty is only available when the search return no customer
            if bs.find('td', {'class', 'empty'}) is not None:
                return {'status':404}
            else:
                return self.parse_single_customer(bs)

        except (urllib2.HTTPError), e:
            return {'status':e.code()}

    def find_by_billing_id(self, billingid):
        uri = '/AdminTool2/base/Customer.details.action'
        uriencoding = {'billingId':billingid}

        try:
            if not self.SessionHandler.sessionstate:
                return {'status':503}

            f = urllib2.urlopen(self.SessionHandler.baseurl + uri, urllib.urlencode(uriencoding))
            if f.getcode() == 403:
                return {'status':403}
            bs = BeautifulSoup(f, "html.parser")

            parent = bs.find('table', {'class', 'wwFormTable'}).find_parent()
            for trs in parent.find_all('tr'):
                tds = trs.find_all('td')
                if len(tds) == 2:
                    if 'Customer ID' in tds[0].text.strip() and tds[1].text.strip() == '':
                        return {'status':404}
            else:
                return self.parse_single_customer(bs)

        except (urllib2.HTTPError), e:
            return {'status':e.code()}

    def find_by_ssn(self,ssn):
        pass


# --------TODO : Remove when API is finished!
if __name__=="__main__":
    argparser = ArgumentParser()
    argparser.add_argument("-u", "--username", dest="username", required=True, help="User to use in J_Auth")
    args = argparser.parse_args()

    password = getpass("Please enter password for %s: " % args.username)

    s = SessionHandler(args.username, password)
    s.login()
    a = Access(s)
    c = Customer(s)


    #received = a.find_by_access_id('35998200')
    #received = c.find_by_customer_id('100')
    received = c.find_by_billing_id('314')
    print received
