"""
Provides tools for initiating webservices session
"""
from xml.etree import ElementTree as xml
import datetime
import logging
from xml.etree.ElementTree import XMLParser
import os
import yaml
from suds.client import Client

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(logging.NullHandler())


def element_to_dict(element):
    e_dict = {a.text: v.text for a, v in zip(element.getiterator('AttrName'), element.getiterator('AttrValue'))}
    return e_dict


def get_session_from_yaml(yaml_file, session_name):
    if os.path.exists(yaml_file):
        f = open(yaml_file)
        session_data = yaml.load(f)[session_name]
        return SoapSession(session_data['user'], session_data['password'],
                           session_data['host'], session_data['port'])
    else:
        raise NameError("File Does not exist {}".format(yaml_file))


def extract_fields(objXML, attribute_only=False):
    x = XMLParser(encoding='utf-8')
    element_list = xml.fromstring(objXML.encode('utf-8'))
    obj_list = []
    if attribute_only:
        obj_list.append({i.tag: i.attrib for i in element_list[0].getchildren()})
    else:
        for e in element_list.iter('UDSObject'):
            element_dict = element_to_dict(e)
            element_dict['handle'] = e[0].text
            obj_list.append(element_dict)
    return obj_list


class SoapResponse(object):
    def __init__(self, xmlresponse, attribute_only=False):
        self.response = xmlresponse
        self.attribute_only = attribute_only

    def __repr__(self):
        if not self.response:
            return ''
        if str(self.response.__class__) == "<class 'suds.sudsobject.reply'>":
            return self.response.__repr__()
        else:
            return self.response

    def __str__(self):
        if not self.response:
            return ""
        if str(self.response.__class__) == "<class 'suds.sudsobject.reply'>":
            return self.response.__str__()
        else:
            return self.response

    def to_dict(self):
        """
        converts raw xml to dictionary
        :return:
        """
        if self.response:
            return extract_fields(objXML=self.response, attribute_only=self.attribute_only)
        else:
            return {}

class SoapService(object):
    def __init__(self, session, service_name):
        self.s = session
        self.name = service_name

    def __call__(self, *args, **kwargs):
        service_method = getattr(self.s.client.service, self.name)
        new_args = list(args)
        for idx, arg in enumerate(new_args):
            if isinstance(arg, list):
                new_args[idx] = self.createArrayOfString(arg)
        for k, v in kwargs.items():
            if isinstance(v, list):
                kwargs[k] = self.createArrayOfString(v)
        return SoapResponse(service_method(self.s.sid, *new_args, **kwargs))

    def createArrayOfString(self, iterable):
        array_of_string = self.s.client.factory.create('ArrayOfString')
        iterable = iterable if iterable else []
        array_of_string.string = iterable
        return array_of_string


def login(host, username, password, port=8080):
    return SoapAPI(SoapSession(username, password, host, port))


class SoapSession(object):
    """This builds a session object used to login to ca webservices."""

    def __init__(self, username, password, host, port=8080, auto_renew=True):
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self._sid = None
        self.url = self.get_url()
        self.client = Client(self.url)
        self.last_login = None
        self.auto_renew = auto_renew

    def __repr__(self):
        return "{}@{}".<EMAIL>(self.username, self.host)

    @property
    def sid(self):
        self._sid = self.login()
        return self._sid

    def login(self):
        if not (self.username and self.password):
            raise ValueError('Invalid username or password')
        if self.session_is_expired():
            newsid = self.client.service.login(self.username, self.password)
            self.last_login = datetime.datetime.now()
            return newsid
        else:
            return self._sid

    def session_is_expired(self):
        """
        Takes subtracts last time logged in from current date and
        if the difference in minutes is greater than 90 minutes
        the session has expired.
        :return:
        """
        if not self.last_login:
            return True
        session_limit = 90
        session_duration = datetime.datetime.now() - self.last_login
        return session_duration.seconds / 60 > session_limit

    def get_url(self):
        url = "http://{}:{}/axis/services/USD_R11_WebService?wsdl"
        if not (self.host or self.port):
            raise ValueError("No valid host name supplied")
        else:
            return url.format(self.host, self.port)


class SoapAPI(object):
    """
    Web Services Interface
    """

    def __init__(self, session):
        # returns suds client interface and sid
        self.cl = session.client
        self.sid = session.sid
        self._session = session

    def __getattr__(self, service_name):
        return SoapService(self._session, service_name)

    #def searchObjects(self, objType, searchCriteria, maxRows=-1, returnAttributes=None):
    #    returnAttributes = returnAttributes if returnAttributes else []
    #    return self.doSelect(objType, searchCriteria, maxRows, attributes=returnAttributes).to_dict()

    def searchObjects(self, objType, searchCriteria, maxRows=-1,returnAttributes=None):
        returnAttributes = returnAttributes if returnAttributes else []
        if (maxRows > 250):
            return_obj = []
            query = self.cl.service.doQuery(self.sid, objType, searchCriteria)
            for x in range(0,query.listLength+1,250):
                   max = x+249
                   if (max > query.listLength-1):
                        max = query.listLength-1
                   if (x < query.listLength):
                        return_obj = return_obj+self.getListValues(query.listHandle,x,max,returnAttributes).to_dict()
            self.freeListHandles(query.listHandle)
            return return_obj
        else:
            return self.doSelect(objType, searchCriteria, maxRows, attributes=returnAttributes).to_dict()

    def updateObject(self, obj_handle, attribute_changes, return_attributes=None):
        attribute_changes = self.createArrayOfString(attribute_changes)
        return_attributes = self.createArrayOfString(return_attributes)
        return self.cl.service.updateObject(self.sid, obj_handle, attribute_changes, return_attributes)

    def createRequest(self, creator_handle, attrvals, return_attributes=None,string_template="",attributes=None,
                      reqHandle="", reqNumber=""):
        attrvals = self.createArrayOfString(attrvals)
        return_attributes = self.createArrayOfString(return_attributes)
        attributes = self.createArrayOfString(attributes)
        results=self.cl.service.createRequest(self.sid, creator_handle, attrvals,return_attributes,string_template,attributes,
                                      reqHandle,reqNumber)
        return results

    def listAttributes(self, obj_name, convert_to_dict=True):
        objresults = self.getObjectTypeInformation(obj_name)
        if convert_to_dict:
            attributes = extract_fields(objXML=objresults.response, attribute_only=convert_to_dict)
        else:
            return objresults.response
        return attributes

    def extractHandle(self, suds_xml):
        return xml.fromstring(suds_xml)[0][0].text

    def isMember(self, user, group):
        ismember = self.doSelect("grpmem", "member = U'{}' and group = U'{}'".format(user, group), )
        if ismember:
            logger.debug("User: {} is in group: {}".format(user, group))
        else:
            logger.debug("User {} Not in group: {}".format(user, group))
        return ismember

    def addToGroup(self, user, group):
        logger.info("Adding user: {} to group {}".format(user, group))
        if not self.isMember(user, group):
            self.cl.service.addMemberToGroup(self.sid, "cnt:{}".format(user), "cnt:{}".format(group))

    def createArrayOfString(self, iterable):
        array_of_string = self.cl.factory.create('ArrayOfString')
        iterable = iterable if iterable else []
        array_of_string.string = iterable
        return array_of_string

    def removeFromGroup(self, user, group):
        logger.info("removing user: {} from group {}".format(user, group))
        if self.isMember(user, group):
            self.removeMemberFromGroup("cnt:{}".format(user), "cnt:{}".format(group))

    def updateRequest(self, ref_num, **kwargs):
        logger.info("updating Request {}".format(ref_num))
        request_handle = self.searchObjects('cr', "ref_num='{}'".format(ref_num), maxRows=1)[0]['handle']
        update_attributes = list(kwargs.items())
        results = self.updateObject(request_handle, update_attributes)
        logger.info("Update Successful")
        return results


    def tansferRequest(self, ref_num, group=None, assignee=None, message=""):
        request_handle = self.searchObjects('cr', "ref_num='{}'".format(ref_num), maxRows=1)[0]['handle']
        user_handle = self.cl.service.getHandleForUserid(self.sid, self._session.username)
        assignee_handle = "cnt:{}".format(assignee) if assignee else ""
        group_handle = "cnt:{}".format(group) if assignee_handle else ""
        setAssignee = 1 if assignee else 0
        setGroup = 1 if group else 0
        if group or assignee:
            self.cl.service.transfer(self.sid, user_handle, request_handle,
                                     message, setAssignee, assignee_handle,
                                     setGroup, group_handle, 0, "")
        else:
            logger.exception("No Group or assignee specified")


if __name__ == '__main__':
    pass

"""
Provides tools for initiating webservices session
"""
from xml.etree import ElementTree as xml
import datetime
import logging
from xml.etree.ElementTree import XMLParser
import os
import yaml
from suds.client import Client

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(logging.NullHandler())


def element_to_dict(element):
    e_dict = {a.text: v.text for a, v in zip(element.getiterator('AttrName'), element.getiterator('AttrValue'))}
    return e_dict


def get_session_from_yaml(yaml_file, session_name):
    if os.path.exists(yaml_file):
        f = open(yaml_file)
        session_data = yaml.load(f)[session_name]
        return SoapSession(session_data['user'], session_data['password'],
                           session_data['host'], session_data['port'])
    else:
        raise NameError("File Does not exist {}".format(yaml_file))


def extract_fields(objXML, attribute_only=False):
    x = XMLParser(encoding='utf-8')
    element_list = xml.fromstring(objXML.encode('utf-8'))
    obj_list = []
    if attribute_only:
        obj_list.append({i.tag: i.attrib for i in element_list[0].getchildren()})
    else:
        for e in element_list.iter('UDSObject'):
            element_dict = element_to_dict(e)
            element_dict['handle'] = e[0].text
            obj_list.append(element_dict)
    return obj_list


class SoapResponse(object):
    def __init__(self, xmlresponse, attribute_only=False):
        self.response = xmlresponse
        self.attribute_only = attribute_only

    def __repr__(self):
        if not self.response:
            return ''
        if str(self.response.__class__) == "<class 'suds.sudsobject.reply'>":
            return self.response.__repr__()
        else:
            return self.response

    def __str__(self):
        if not self.response:
            return ""
        if str(self.response.__class__) == "<class 'suds.sudsobject.reply'>":
            return self.response.__str__()
        else:
            return self.response

    def to_dict(self):
        """
        converts raw xml to dictionary
        :return:
        """
        if self.response:
            return extract_fields(objXML=self.response, attribute_only=self.attribute_only)
        else:
            return {}

class SoapService(object):
    def __init__(self, session, service_name):
        self.s = session
        self.name = service_name

    def __call__(self, *args, **kwargs):
        service_method = getattr(self.s.client.service, self.name)
        new_args = list(args)
        for idx, arg in enumerate(new_args):
            if isinstance(arg, list):
                new_args[idx] = self.createArrayOfString(arg)
        for k, v in kwargs.items():
            if isinstance(v, list):
                kwargs[k] = self.createArrayOfString(v)
        return SoapResponse(service_method(self.s.sid, *new_args, **kwargs))

    def createArrayOfString(self, iterable):
        array_of_string = self.s.client.factory.create('ArrayOfString')
        iterable = iterable if iterable else []
        array_of_string.string = iterable
        return array_of_string


def login(host, username, password, port=8080):
    return SoapAPI(SoapSession(username, password, host, port))


class SoapSession(object):
    """This builds a session object used to login to ca webservices."""

    def __init__(self, username, password, host, port=8080, auto_renew=True):
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self._sid = None
        self.url = self.get_url()
        self.client = Client(self.url)
        self.last_login = None
        self.auto_renew = auto_renew

    def __repr__(self):
        return "{}@{}".<EMAIL>(self.username, self.host)

    @property
    def sid(self):
        self._sid = self.login()
        return self._sid

    def login(self):
        if not (self.username and self.password):
            raise ValueError('Invalid username or password')
        if self.session_is_expired():
            newsid = self.client.service.login(self.username, self.password)
            self.last_login = datetime.datetime.now()
            return newsid
        else:
            return self._sid

    def session_is_expired(self):
        """
        Takes subtracts last time logged in from current date and
        if the difference in minutes is greater than 90 minutes
        the session has expired.
        :return:
        """
        if not self.last_login:
            return True
        session_limit = 90
        session_duration = datetime.datetime.now() - self.last_login
        return session_duration.seconds / 60 > session_limit

    def get_url(self):
        url = "http://{}:{}/axis/services/USD_R11_WebService?wsdl"
        if not (self.host or self.port):
            raise ValueError("No valid host name supplied")
        else:
            return url.format(self.host, self.port)


class SoapAPI(object):
    """
    Web Services Interface
    """

    def __init__(self, session):
        # returns suds client interface and sid
        self.cl = session.client
        self.sid = session.sid
        self._session = session

    def __getattr__(self, service_name):
        return SoapService(self._session, service_name)

    #def searchObjects(self, objType, searchCriteria, maxRows=-1, returnAttributes=None):
    #    returnAttributes = returnAttributes if returnAttributes else []
    #    return self.doSelect(objType, searchCriteria, maxRows, attributes=returnAttributes).to_dict()

    def searchObjects(self, objType, searchCriteria, maxRows=-1,returnAttributes=None):
        returnAttributes = returnAttributes if returnAttributes else []
        if (maxRows > 250):
            return_obj = []
            query = self.cl.service.doQuery(self.sid, objType, searchCriteria)
            for x in range(0,query.listLength+1,250):
                   max = x+249
                   if (max > query.listLength-1):
                        max = query.listLength-1
                   if (x < query.listLength):
                        return_obj = return_obj+self.getListValues(query.listHandle,x,max,returnAttributes).to_dict()
            self.freeListHandles(query.listHandle)
            return return_obj
        else:
            return self.doSelect(objType, searchCriteria, maxRows, attributes=returnAttributes).to_dict()

    def updateObject(self, obj_handle, attribute_changes, return_attributes=None):
        attribute_changes = self.createArrayOfString(attribute_changes)
        return_attributes = self.createArrayOfString(return_attributes)
        return self.cl.service.updateObject(self.sid, obj_handle, attribute_changes, return_attributes)

    def createRequest(self, creator_handle, attrvals, return_attributes=None,string_template="",attributes=None,
                      reqHandle="", reqNumber=""):
        attrvals = self.createArrayOfString(attrvals)
        return_attributes = self.createArrayOfString(return_attributes)
        attributes = self.createArrayOfString(attributes)
        results=self.cl.service.createRequest(self.sid, creator_handle, attrvals,return_attributes,string_template,attributes,
                                      reqHandle,reqNumber)
        return results

    def listAttributes(self, obj_name, convert_to_dict=True):
        objresults = self.getObjectTypeInformation(obj_name)
        if convert_to_dict:
            attributes = extract_fields(objXML=objresults.response, attribute_only=convert_to_dict)
        else:
            return objresults.response
        return attributes

    def extractHandle(self, suds_xml):
        return xml.fromstring(suds_xml)[0][0].text

    def isMember(self, user, group):
        ismember = self.doSelect("grpmem", "member = U'{}' and group = U'{}'".format(user, group), )
        if ismember:
            logger.debug("User: {} is in group: {}".format(user, group))
        else:
            logger.debug("User {} Not in group: {}".format(user, group))
        return ismember

    def addToGroup(self, user, group):
        logger.info("Adding user: {} to group {}".format(user, group))
        if not self.isMember(user, group):
            self.cl.service.addMemberToGroup(self.sid, "cnt:{}".format(user), "cnt:{}".format(group))

    def createArrayOfString(self, iterable):
        array_of_string = self.cl.factory.create('ArrayOfString')
        iterable = iterable if iterable else []
        array_of_string.string = iterable
        return array_of_string

    def removeFromGroup(self, user, group):
        logger.info("removing user: {} from group {}".format(user, group))
        if self.isMember(user, group):
            self.removeMemberFromGroup("cnt:{}".format(user), "cnt:{}".format(group))

    def updateRequest(self, ref_num, **kwargs):
        logger.info("updating Request {}".format(ref_num))
        request_handle = self.searchObjects('cr', "ref_num='{}'".format(ref_num), maxRows=1)[0]['handle']
        update_attributes = list(kwargs.items())
        results = self.updateObject(request_handle, update_attributes)
        logger.info("Update Successful")
        return results


    def tansferRequest(self, ref_num, group=None, assignee=None, message=""):
        request_handle = self.searchObjects('cr', "ref_num='{}'".format(ref_num), maxRows=1)[0]['handle']
        user_handle = self.cl.service.getHandleForUserid(self.sid, self._session.username)
        assignee_handle = "cnt:{}".format(assignee) if assignee else ""
        group_handle = "cnt:{}".format(group) if assignee_handle else ""
        setAssignee = 1 if assignee else 0
        setGroup = 1 if group else 0
        if group or assignee:
            self.cl.service.transfer(self.sid, user_handle, request_handle,
                                     message, setAssignee, assignee_handle,
                                     setGroup, group_handle, 0, "")
        else:
            logger.exception("No Group or assignee specified")


if __name__ == '__main__':
    pass

Provides tools for initiating webservices session converts raw xml to dictionary         :return: This builds a session object used to login to ca webservices. Takes subtracts last time logged in from current date and         if the difference in minutes is greater than 90 minutes         the session has expired.         :return: Web Services Interface # returns suds client interface and sid #def searchObjects(self, objType, searchCriteria, maxRows=-1, returnAttributes=None): #    returnAttributes = returnAttributes if returnAttributes else [] #    return self.doSelect(objType, searchCriteria, maxRows, attributes=returnAttributes).to_dict()