Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Excerpt

This example written in Python allows users to manage VoipNow accounts and fetch call statistics. Download.

Table of Contents
typelist

Note

This demonstrative code SHOULD NOT be used in production. It is designed to show how a client application can interact with the VoipNow SystemAPI. From this perspective, validations and error-checks aiming to demonstrate the most common mistakes are minimal and can be done easily.

How To Install It

In order to be able to use the SystemAPI Python Tool, you must first check what components you need depending on your operating system and after that follow the installation steps.

System Requirements for Linux

System Requirements for Microsoft Windows

Setup

Download the tool files.

Note

During the installation procedure, you will be asked to extract some archives. The folder names and locations are not important. You simply need to run the commands shown below.

Linux

STEP 1: As soon as gcc and Python are installed and fpconst is downloaded, please extract the contents of the fpconst archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install

To check that fpconst has been properly installed, type the following in your Python command-line:

Code Block
languagepython
import fpconst

If you don't have any error messages, fpconst is correctly installed.

STEP 2: As soon as PyXML is downloaded, please extract the contents of the archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install

To check that PyXML has been properly installed, type the following in your Python command-line:

Code Block
languagepython
import xml

If you do not have any error messages, PyXML is correctly installed.

STEP 3: As soon as SOAPpy is downloaded, please extract the contents of the archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install

To check that SOAPpy has been properly installed, type the following in your Python command-line:

Code Block
languagepython
import SOAPpy

If you do not have any error messages, SOAPpy is correctly installed.

Warning

If you get the following error during installation:
File ".../SOAPpy/Client.py", line 46
from _future_ import nested_scopes
SyntaxError: from _future_ imports must occur at the beginning of the file
You need to open your temporary folder SOAPpy\Client.py and copy/paste the "from _future_ imports" line to be the first import.
The same error will appear with other files during installation; therefore, it is recommended that you repeat this step.

Note

The Client.py is just a temporary folder used by the SOAPpy engine. You don't need to bother with it.

STEP 4: As soon as ZSI is downloaded, please extract the contents of the archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install

To check that ZSI has been properly installed, type the following in your Python command-line:

Code Block
languagepython
import ZSI

If you do not have any error messages, ZSI is correctly installed.

STEP 5: You can start writing applications for your VoipNow server using the SOAPpy web services toolkit.

Microsoft Windows

STEP 1: As soon as Visual Studio 2008 Express and Python are installed and fpconst is downloaded, please extract the contents of the fpconst archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install

STEP 2: As soon as PyXML is downloaded, you need to run the installer:

Note

There are different PyXML installers for various Python versions, so make sure you download the appropriate one, otherwise the installer will not work. If you're not sure what to do, please visit the links in the README file.

STEP 3: As soon as SOAPpy is downloaded, please extract the contents of the archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install
Warning

If you get the following error during installation:
File ".../SOAPpy/Client.py", line 46
from _future_ import nested_scopes
SyntaxError: from _future_ imports must occur at the beginning of the file
You must open your temporary folder SOAPpy\Client.py and copy/paste the "from _future_ imports" line to be the first import.
The same error will appear with other files during installation; therefore, it is recommended that you repeat this step.

STEP 4: As soon as ZSI is downloaded, please extract the contents of the archive, open the extracted folder, and run the following commands:

Code Block
python
python
python setup.py build
python setup.py install

STEP 5: You can start writing applications for your VoipNow server using the SOAPpy web services toolkit.

Note

When writing applications, you may sometimes encounter the following error:

Code Block
Traceback (most recent call last):
File "pythonDemo.py", line 61, in ?
server._callWithBody(body1)
File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py", line 436, in _callWithBody
return self.__call(None, body, {})
File "/usr/lib/python2.4/site-packages/SOAPpy/Client.py", line 395, in __call
p, attrs = parseSOAPRPC(r, attrs = 1)
File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py", line 1049, in parseSOAPRPC
t = _parseSOAP(xml_str, rules = rules)
File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py", line 1029, in _parseSOAP
parser.parse(inpsrc)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/expatreader.py", line 109, in parse
xmlreader.IncrementalParser.parse(self, source)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/xmlreader.py", line 123, in parse
self.feed(buffer)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/expatreader.py", line 216, in feed
self._parser.Parse(data, isFinal)
File "/usr/lib/python2.4/site-packages/_xmlplus/sax/expatreader.py", line 363, in end_element_ns
self._cont_handler.endElementNS(pair, None)
File "/usr/lib/python2.4/site-packages/SOAPpy/Parser.py", line 235, in endElementNS
kind = (self._prem[kind[:i]], kind[i + 1:])
KeyError: u'http'

If this happens to you, please make sure you download the latest patch for the Parser.py file from the SOAPpy library.
You may find it here: http://pywebsvcs.svn.sourceforge.net/viewvc/pywebsvcs/trunk/SOAPpy/SOAPpy/Parser.py?view=markup&pathrev=1497

How To Use It

Authentication

First you need to create a headerType object.

Code Block
python
python
header1 = SOAPpy.Types.headerType()

Then you need to fill in the user credentials. VoipNow the OAuth protocol, where the user must provide his access token. The old legacy authentication and OAuth with consumer key and consumer secret are deprecated.

The code should look something like this:

Code Block
python
python
# Authentication data
ACCESS_TOKEN = "CHANGEME"
 
#XML template for sending the authentication data
authenticationTemplate = """ <ns1:accessToken>%s</ns1:accessToken>"""
  				
# filling in the template above with our authentication data				
credentials = authenticationTemplate % (ACCESS_TOKEN)
soapHeader.userCredentials = SOAPpy.Types.headerType(credentials)

# the namespace for the user credentials
soapHeader.userCredentials._ns = "http://4psa.com/HeaderData.xsd/3.0.0"
soapHeader.userCredentials.userCredentials = SOAPpy.Types.untypedType(credentials)

Examples

Add a Service Provider

The following example shows how to add a new service provider. It also fetches the list of charging plans in order to pick a random one to use with the new service provider. For more information on how to get the access token, please check the access management documentation. The code should look like this:

Code Block
python
python
"""
4PSA VoipNow SystemAPI Client for Python
Copyright (c) 2013, Rack-Soft (www.4psa.com). All rights reserved.
VoipNow is a Trademark of Rack-Soft, Inc
4PSA is a Registered Trademark of Rack-Soft, Inc.
All rights reserved.
This script adds a service provider.
"""

import sys
import string
import random
import SOAPpy
from SOAPpy import SOAPProxy

# To view soap request/response set SOAPpy.Config.debug = 1
SOAPpy.Config.debug = 0

# Authentication data
ACCESS_TOKEN = "CHANGEME"

# Start constructing the Header
soapHeader = SOAPpy.Types.headerType()

#XML template for sending the authentication data
authenticationTemplate = """ <ns1:accessToken>%s</ns1:accessToken>"""
  				
# filling in the template above with our authentication data				
credentials = authenticationTemplate % (ACCESS_TOKEN)
soapHeader.userCredentials = SOAPpy.Types.headerType(credentials)

# the namespace for the user credentials
soapHeader.userCredentials._ns = "http://4psa.com/HeaderData.xsd/3.0.0"
soapHeader.userCredentials.userCredentials = SOAPpy.Types.untypedType(credentials)

# We need a charging plan for the new account, so we make a request to
# fetch all the charging plans and then pick a random one from the response list.

# Construct the Body
soapBody = SOAPpy.Types.untypedType("")
soapBody._name = "ns2:GetChargingPlans" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/BillingMessages.xsd/3.0.0")

# Set endpoint
endpoint = "https://voipnow2demo.4psa.com/soap2/billing_agent.php"

# Set soapaction
soapaction = "http://4psa.com/Billing/3.0.0:getChargingPlansIn"

# Set service connection
server = SOAPProxy(endpoint, noroot = 1, soapaction = soapaction, header = soapHeader)

#run our soap request
try:
	chargingPlans = server._callWithBody(soapBody)
except SOAPpy.Types.faultType, error:
	# Catch exception, for situations when the charging plans could not be fetched
	print "Error " + error[0] + ": " + error[1] 
	sys.exit(1)

# Get the id of a random charging plan
chargingPlanID = None
if len(chargingPlans) != 0:
	randChargingPlan = chargingPlans[random.randint(0, len(chargingPlans) - 1)]
	if hasattr(randChargingPlan, 'ID'):
		chargingPlanID = randChargingPlan.ID
		if hasattr(randChargingPlan, 'name'):
			print "Using charging plan " + randChargingPlan.name + "."
		else:
			print "Using charging plan with id " + chargingPlanID + "."
	
#XML template for sending the new service provider data
bodyTemplate = """
      <ns3:name>%s</ns3:name>
      <ns3:login>%s</ns3:login>
      <ns3:password>%s</ns3:password>
      <ns3:country>%s</ns3:country>"""

if chargingPlanID != None:
	bodyTemplate += """
		  <ns3:chargingPlanID>%s</ns3:chargingPlanID>"""
	  
# Service Provider data
name = "SPPython" + str(random.randint(1, 1000))
login = "SPPython" + str(random.randint(1, 1000))
password = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
country = "us"

# Filling in the template above with the service provider data
if chargingPlanID != None:
	body = bodyTemplate % (name, login, password, country, chargingPlanID)
else:
	body = bodyTemplate % (name, login, password, country)

soapBody = SOAPpy.Types.untypedType(body)
soapBody._name = "ns2:AddServiceProvider" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/ServiceProviderMessages.xsd/3.0.0")
soapBody._setAttr("xmlns:ns3", "http://4psa.com/ServiceProviderData.xsd/3.0.0")

# Set endpoint
endpoint = "https://voipnow2demo.4psa.com/soap2/sp_agent.php"

# Set soapaction
soapaction = "http://4psa.com/ServiceProvider/3.0.0:addServiceProviderIn"

# Set service connection
server = SOAPProxy(endpoint, noroot = 1, soapaction = soapaction, header = soapHeader)

#run our soap request
try:
	server._callWithBody(soapBody)
	print "Service provider created successfully."
except SOAPpy.Types.faultType, error:
	# Catch exception, for situations when the service provider could not be added
	print "Error " + error[0] + ": " + error[1] 

Adding Other Account Types

Tip

These examples can be found in the package downloaded with the SystemAPI Python Tool.

If you wish to add other account types (organizations, users or extensions), there is not much to change from the program listed above. First of all, you will also need the id of a parent account. This stands for the ID of the service provider that owns the organization (when adding a new organization) or the ID of the organization that owns the user (when adding a new user) or the ID of the user that owns the extension (when adding a new extension). Therefore, the list of appropriate accounts will be fetched and a random one will be chosen as the parent for the new account:

Code Block
languagepython
# We need a parent service provider for the new organization, so we make a request to
# fetch all the service providers.

# Construct the Body
soapBody = SOAPpy.Types.untypedType("")
soapBody._name = "ns2:GetServiceProviders" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/ServiceProviderMessages.xsd/3.0.0")

# Set endpoint
endpoint = "https://voipnow2demo.4psa.com/soap2/sp_agent.php"

# Set soapaction
soapaction = "http://4psa.com/ServiceProvider/3.0.0:getServiceProvidersIn"

# Set service connection
server = SOAPProxy(endpoint, noroot = 1, soapaction = soapaction, header = soapHeader)

#run our soap request
try:
	serviceProviders = server._callWithBody(soapBody)
except SOAPpy.Types.faultType, error:
	# Catch exception, for situations when the service providers could not be fetched
	print "Error " + error[0] + ": " + error[1]
	sys.exit(1)

# Get the id of a random service provider
serviceProviderID = None
if len(serviceProviders) != 0:
	randServiceProvider = serviceProviders[random.randint(0, len(serviceProviders) - 1)]
	if hasattr(randServiceProvider, 'ID'):
		serviceProviderID = randServiceProvider.ID
		if hasattr(randServiceProvider, 'name'):
			print "Using parent service provider " + randServiceProvider.name + "."
		else:
			print "Using parent service provider with id " + serviceProviderID + "."

if serviceProviderID == None:
	print "No service providers found on the server. Can not add an organization."
	sys.exit(1)

You will also need to make adjustments to the parameters of the new request, in order to match the new account type:

Code Block
languagepython
# Organization data
name = "OrgPython" + str(random.randint(1, 1000))
login = "OrgPythong" + str(random.randint(1, 1000))
password = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10))
country = "us"
parentID = serviceProviderID

# Filling in the template above with the organization data
if chargingPlanID != None:
	body = bodyTemplate % (name, login, password, country, parentID, chargingPlanID)
else:
	body = bodyTemplate % (name, login, password, country, parentID)

soapBody = SOAPpy.Types.untypedType(body)
soapBody._name = "ns2:AddOrganization"
 
# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/OrganizationMessages.xsd/3.0.0")
soapBody._setAttr("xmlns:ns3", "http://4psa.com/OrganizationData.xsd/3.0.0")

# Set endpoint
endpoint = "https://voipnow2demo.4psa.com/soap2/organization_agent.php"

# Set soapaction
soapaction = "http://4psa.com/Organization/3.0.0:addOrganizationIn"

Adding an user or an extension is very similar. You only need to adjust the namespaces, endpoint and soapaction variables. Also, for extension accounts, you should use the label field instead of name and login.

CallCosts

The DEMO version also provides a CallCosts request. Here is the code:

Code Block
languagepython
"""
4PSA VoipNow SystemAPI Client for Python
Copyright (c) 2013, Rack-Soft (www.4psa.com). All rights reserved.
VoipNow is a Trademark of Rack-Soft, Inc
4PSA is a Registered Trademark of Rack-Soft, Inc.
All rights reserved.
This script fetches the call costs of an extension.
"""

import sys
import random
import datetime
import SOAPpy
from SOAPpy import SOAPProxy

# To view soap request/response set SOAPpy.Config.debug = 1
SOAPpy.Config.debug = 0

# Authentication data
ACCESS_TOKEN = "CHANGEME"

# Start constructing the Header
soapHeader = SOAPpy.Types.headerType()

#XML template for sending the authentication data
authenticationTemplate = """ <ns1:accessToken>%s</ns1:accessToken>"""
  				
# filling in the template above with our authentication data				
credentials = authenticationTemplate % (ACCESS_TOKEN)
soapHeader.userCredentials = SOAPpy.Types.headerType(credentials)

# the namespace for the user credentials
soapHeader.userCredentials._ns = "http://4psa.com/HeaderData.xsd/3.0.0"
soapHeader.userCredentials.userCredentials = SOAPpy.Types.untypedType(credentials)

# We need an extension to check its call costs, so we make a request to
# fetch all the extensions.

# Construct the Body
soapBody = SOAPpy.Types.untypedType("")
soapBody._name = "ns2:GetExtensions" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/ExtensionMessages.xsd/3.0.0")

# Set endpoint
endpoint = "https://voipnow2demo.4psa.com/soap2/extension_agent.php"

# Set soapaction
soapaction = "http://4psa.com/User/3.0.0:getExtensionsIn"

# Set service connection
server = SOAPProxy(endpoint, noroot = 1, soapaction = soapaction, header = soapHeader)

#run our soap request
try:
	extensions = server._callWithBody(soapBody)
except SOAPpy.Types.faultType, error:
	# Catch exception, for situations when the extensions could not be fetched
	print "Error " + error[0] + ": " + error[1]
	sys.exit(1)

# Get the identifier of a random extension
extensionIdentifier = None
if len(extensions) != 0:
	randExtension = extensions[random.randint(0, len(extensions) - 1)]
	if hasattr(randExtension, 'identifier'):
		extensionIdentifier = randExtension.identifier
		if hasattr(randExtension, 'name'):
			print "Fetching call costs for extension " + randExtension.name + "."
		else:
			print "Fetching call costs for extension with identifier " + extensionIdentifier + "."

if extensionIdentifier == None:
	print "No extensions found on the server. Can not make the call costs request."
	sys.exit(1)

# Construct the Body
body = SOAPpy.Types.structType()

#XML template for sending the new request data
bodyTemplate = """
      <userIdentifier>%s</userIdentifier>
	  <interval>
			<startDate>%s</startDate>
			<endDate>%s</endDate>
	  </interval>"""

# The current date, used for the request data
now = datetime.datetime.now()
	  
# Request data
userIdentifier = extensionIdentifier
startDate = "2012-01-01"
endDate = now.strftime("%Y-%m-%d")

# Filling in the template above with the request data
body = bodyTemplate % (userIdentifier, startDate, endDate)
soapBody = SOAPpy.Types.untypedType(body)
soapBody._name = "ns2:CallCosts" 

# Set namespaces
soapBody._setAttr("xmlns:ns2", "http://4psa.com/ReportMessages.xsd/3.0.0")

# Set endpoint
endpoint = "https://voipnow2demo.4psa.com/soap2/report_agent.php"

# Set soapaction
soapaction = "http://4psa.com/Report/3.0.0:CallCostsIn"

# Set service connection
server = SOAPProxy(endpoint, noroot = 1, soapaction = soapaction, header = soapHeader)

#run our soap request
try:
	callCosts = server._callWithBody(soapBody)
	# Display the result
	print callCosts.totalCalls + " calls have been made between " + startDate + " and " + endDate + " with a total cost of " + callCosts.cost + " " + callCosts.currency
except SOAPpy.Types.faultType, error:
	# Catch exception, for situations when the call costs could not be fetched
	print "Error " + error[0] + ": " + error[1]
	sys.exit(1)

Except where otherwise noted, content in this space is licensed under a Creative Commons Attribution 4.0 International.