Assetic REST API Tutorial (Python)
The following examples expand on the Quick Start examples by providing more detail about how the Assetic Python SDK utilises the Assetic REST API.
This tutorial provides sample code for completing the following common tasks with the Assetic Python SDK:
- Getting data from Assetic
- Updating data in Assetic
- Creating data in Assetic
- Logging to email
- Return data in XML format
Getting Data from Assetic using Assetic SDK
The following example gets the work order details for a given work order Id. Note that the Id is a GUID string.
Replace the following placeholder in the script with your information:your_config_path.
To learn how the script works, see the inline comments (which start with # in Python) as well as the explanations that follow.
- """
- Example script to get work order using guid (Assetic.WorkOrderGetAPI.py)
- """
- import assetic
- ##Assetic SDK instance
- asseticsdk = assetic.AsseticSDK("C:/Users/sjsam/Documents/rest-api-examples/Python/assetic.ini",None,"Info")
- #instance of work order API
- wkoapi = assetic.WorkOrderApi()
- ##unique ID of work order
- woguid = "115ee90b-7ede-4e00-8ad2-0001f6646bab"
- try:
- wko = wkoapi.work_order_integration_api_get_0(woguid)
- except assetic.rest.ApiException as e:
- asseticsdk.logger.error("Status {0}, Reason: {1}".format(e.status,e.reason))
- else:
- ##output the response to the logger
- asseticsdk.logger.info(wko.get("WorkOrderStatus"))
- asseticsdk.logger.info(wko.get("FriendlyId"))
How it Works
The script first imports the assetic module. Next it initiates the assetic instance by providing the location of the ini file ("/your_config_path/assetic.ini") containing the API Key and environment URL variables we defined in our "assetic.ini" file. These variables are used for authenticating the Assetic REST API queries.
The second parameter sets the logging output. In this example it is "None", so the default is to log output to the console. Alternatively a log file name (including folder path) could be set defined so that the logging is written to a text file.
The third parameter is the the debugging level for the logger which is set as "Info" in this example
import assetic ##Assetic SDK instance asseticsdk = assetic.AsseticSDK("/your_config_path/assetic.ini",None,"Info")
The next step in the script is to set the work order Id that will be searched and also create an instance of the work order API module.
#instance of work order API wkoapi = assetic.WorkOrderApi() ##unique ID of work order woguid = "115ee90b-7ede-4e00-8ad2-0001f6646bab"
Now that the work order API is configured, the method "work_order_get_0" can be used to retrieve the work order. If there is an error the SDK will manage the error and package it as an exception of type assetic.rest.ApiException. If there is no error the WorkOrderStatus and Friendly Id is extracted from the response and displayed to the console via the logger. If there is an error the error message is displayed to the console via the logger.
try: wko = wkoapi.work_order_get_0(woguid) except assetic.rest.ApiException as e: asseticsdk.logger.error("Status {0}, Reason: {1}".format(e.status,e.reason)) else: ##output the response to the logger asseticsdk.logger.info(wko.get("WorkOrderStatus")) asseticsdk.logger.info(wko.get("FriendlyId"))
Updating Data in Assetic using Assetic SDK
The following example updates the 'External Cost' for a task in a work order.
Before trying out the script, change the value of the id variable and replace the following placeholder: your_config_path.
The "assetic.ini" config file must be created. This is explained in the Quick Start guide Assetic API Access Control.
To learn how the script works, see the inline comments as well as the explanations that follow.
- """
- Example script to apply a work order cost (Assetic.UpdateWorkOrderTask.py)
- Applies an actual cost to a work order task
- """
- import assetic
- ##Assetic SDK instance
- asseticsdk = assetic.AsseticSDK("c:/users/kwilton/assetic.ini",None,"Info")
- ##define work order and task GUID to update against
- woguid = "f6a6a21e-0a23-e611-9458-06edd62954d7" #Preview - COMP stage
- taskid = "f7a6a21e-0a23-e611-9458-06edd62954d7"
- woguid = "46365754-20c3-e611-946c-06edd62954d7" #Demo
- taskid = "5a309e65-20c3-e611-946c-06edd62954d7"
- ##create work order API instance
- wkoapi = assetic.WorkOrderApi()
- #get current values for the task. Use this response object as the base
- #for the update to ensure fields we are not updating are preserved
- try:
- task = wkoapi.work_order_get_work_task_by_work_task_id(woguid,taskid)
- except assetic.rest.ApiException as e:
- asseticsdk.logger.info("Status {0}, Reason: {1}".format(e.status,e.reason))
- exit()
- #update task object to set external_costs to $500, other costs to $200
- task["ParentId"] = woguid
- task["ExternalCosts"] = 500
- task["OtherCosts"] = 200
- ##apply updated task
- try:
- wkoapi.work_order_put_0(woguid,taskid,task)
- except assetic.rest.ApiException as e:
- asseticsdk.logger.info("Status {0}, Reason: {1}".format(e.status,e.reason))
- else:
- asseticsdk.logger.info("Success")
How it Works
The script creates an object instance of the work order task structure by using the Assetic REST SDK to get the work task using the work order GUID and work task GUID. This example assumes these two values are already known by the integrating application.
The task response has the current values for all fields in the task object. This is important because the PUT api will update all fields, not a subset of fields
try: task = wkoapi.work_order_get_work_task_by_work_task_id(woguid,taskid) except assetic.rest.ApiException as e: asseticsdk.logger.info("Status {0}, Reason: {1}".format(e.status,e.reason)) exit()
The fields to be updated are changed for the task object. Note that 'ParentId' (work order GUID) is a mandatory field, but it's value is not returned by the task GET, so it needs to be set. Only 'External Costs' and 'Othr costs' are being updated in the example.
#update task object to set external_costs to $500, other costs to $200 task["ParentId"] = woguid task["ExternalCosts"] = 500 task["OtherCosts"] = 200
The task object is then passed to the work_order_put_0 method that applies the update. This method expects 3 parameters, the work order ID, the task Id, and the task object. Since this method applies an update there is no data returned, just a response code that is handled by assetic.rest.ApiException. If there is no exception then the update is successful.
Note: The Assetic PUT endpoints do not support partial updates so all fields in the payload need to have the updated value, or the original value. In some instances a GET may be used first to use the response as the basis for the PUT. The fields to be updated are changed in the response and the response used for the PUT payload.
The list of fields that are available in the task object could be listed by creating an instance of the object and listing the 'swagger_types' (swagger is the tool used to produce the SDK)
task = assetic.Assetic3IntegrationApiRepresentationsWorkTask()
print task.swagger_types
You can also use the Python inspect tools to list the methods and objects defined in the assetic SDK.
Creating data
The Assetic REST API POST endpoints are used to create data. In the following example a new asset will be created.
- """
- Create an asset (Assetic.AssetsCreate.py)
- """
- import assetic
- #Define location of configuration file, log file, log level
- asseticsdk=assetic.AsseticSDK("C:/users/kwilton/assetic.ini",
- None,"Debug")
- assetapi = assetic.ComplexAssetApi()
- #api has a predefined set of core fields
- #can define additional asset attribute fields
- #attribute field names are the internal field names, get using metadata api
- #create an instance of the complex asset object
- asset = assetic.Assetic3IntegrationRepresentationsComplexAssetRepresentation()
- ##mandatory fields
- asset.asset_category_id='246da33d-6544-e411-82fb-f4b7e2cd8977' #roads
- #asset.asset_category = 'Roads'
- asset.status = 'Active'
- asset.asset_id = 'RINT07'
- asset.asset_name = 'Kevin Road 7'
- ##optional core fields
- asset.asset_class = 'Transport'
- asset.asset_sub_class = 'Road'
- asset.asset_type = 'Unsealed Road'
- asset.asset_sub_type = 'Gravel'
- asset.asset_external_identifier = "ext07"
- asset.asset_criticality = "Arterial"
- asset.asset_maintenance_type = "Road"
- asset.asset_maintenance_sub_type = "Arterial Road"
- asset.asset_work_group = "Roads"
- ##add attributes as a dictionary of field name & field value pairs
- asset.attributes = {"Comment":"a comment","Link":"www.assetic.com"}
- ##now execute the request
- try:
- response = assetapi.complex_asset_post(asset)
- except assetic.rest.ApiException as e:
- asseticsdk.logger.error("Status {0}, Reason: {1}".format(e.status,e.reason))
- exit()
- if response == None:
- asseticsdk.logger.info("Empty response")
- exit()
- data = response.get("Data")
- if data == None:
- asseticsdk.logger.info("Asset not created")
- exit()
- ##display the returned asset data
- for newasset in data:
- assetid = newasset.get("AssetId")
- assetguid = newasset.get("Id")
- assetname = newasset.get("AssetName")
- msg = "guid: {0}, asset {1}, name {2}".format(
- assetguid,assetid,assetname)
- ##now get the asset by specifying asset guid
- try:
- response = assetapi.complex_asset_get(assetguid,["Zone","Locality"])
- except assetic.rest.ApiException as e:
- asseticsdk.logger.error("Status {0}, Reason: {1}".format(e.status,e.reason))
- exit()
- if response == None:
- asseticsdk.logger.info("Empty response")
- exit()
- #display the returned info
- assetid = response.get("AssetId")
- assetguid = response.get("Id")
- assetname = response.get("AssetName")
- attributes = response.get("Attributes")
- zone = attributes.get("Zone")
- locality = attributes.get("Locality")
- asseticsdk.logger.info("Now get the last asset in the list by asset guid")
- msg = "guid: {0}, asset {1}, name {2}, zone {3}, locality {4}".format(
- assetguid,assetid,assetname,zone,locality)
- asseticsdk.logger.info(msg)
- ##get the asset by specifying user friendly asset id
- try:
- response = assetapi.complex_asset_get(assetid,["Zone","Locality"])
- except assetic.rest.ApiException as e:
- asseticsdk.logger.error("Status {0}, Reason: {1}".format(e.status,e.reason))
- exit()
- if response == None:
- asseticsdk.logger.info("Empty response")
- exit()
- #display the returned info
- assetid = response.get("AssetId")
- assetguid = response.get("Id")
- assetname = response.get("AssetName")
- attributes = response.get("Attributes")
- zone = attributes.get("Zone")
- locality = attributes.get("Locality")
- msg = "Now get the last asset in the list by user friendly asset ID"
- asseticsdk.logger.info(msg)
- msg = "guid: {0}, asset {1}, name {2}, zone {3}, locality {4}".format(
- assetguid,assetid,assetname,zone,locality)
- asseticsdk.logger.info(msg)
Logging to Email
Python allows writing of log output to email via SMTP. The AssetSDK has an initialisation method that configures this capability on a per script basis. It requires the SMTP server and credentials to be set in the assetic.ini file.
So that a single script does not send multiple emails a log buffer is used to hold the error messages in memory until the user defined 'capacity' (number of log events) is reached. At that point the messages are written to a single email and sent to one or more recipients. If 'capacity'=1 then every log event is sent as a separate email.
The log level for the emails can be set to a different level to the log file or console output defined when initialising asseticSDK. If there is an error creating the email then an error is written to the log file/console and no further attempt to email is made for the duration of the script.
The format of the log may also differ from the format output to log file/console.
Code Sample
The following code sample initialises email logging at the 'error' level. This means any error will be sent as an email, but other messages will not.
- import assetic
- ##initialise the asseticSDK. Standard logging to the console and error level
- ##is 'info'
- asseticsdk = assetic.AsseticSDK("c:/users/you/assetic.ini",None,"Info")
- ##settings for the email logger
- loglevel = "Error" #python log level
- #set the buffer capacity. In this example on the 10th log 'error' event the
- #email is sent with the last 10 errors. If None then defaults to 1
- capacity = 10
- subject = "Version Integration Error" #subject of email
- from_email = "your_email@yourdomain.com"
- ##send email to one or more recipients
- #to_emails = ["person1@assetic.com","person2@assetic.com"]
- to_emails = ["kwilton@assetic.com"]
- #can optionally set a different log format to the format used in the log file
- #check python docs for formatting options
- logformat = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s"
- asseticsdk.setup_log_to_email(from_email,to_emails,
- subject,capacity,loglevel,logformat)
- versionapi = assetic.VersionApi()
- try:
- asseticsdk.logger.info("Get version information")
- response = versionapi.version_get()
- except assetic.rest.ApiException as e:
- asseticsdk.logger.error("Status {0}, Reason: {1}".format(e.status,e.reason))
- else:
- asseticsdk.logger.info("{0}.{1}.{2}.{3}".format(response["Major"],response["Minor"],
- response["Build"],response["Revision"]))
- #the following line is only needed if running from an editor such as IDLE
- #and you have initiated email logging with a capcaity > 1
- asseticsdk.flush_email_logger()
Return data in XML format
Instead of the default JSON format, the Assetic REST API response may be returned as XML. This may be useful if integrating with an application that requires XML data, or perhaps formatting the response into HTML via XSLT style sheets.
To specify a response in XML, instantiate the Assetic Python SDK as normal, and then specify the 'Accepts' header type on the following line as shown below:
##Assetic SDK instance asseticsdk = assetic.AsseticSDK(None,None,"Info") ##set the response type to XML intead of the default JSON asseticsdk.client.default_headers['Accept'] = 'application/xml'
The following code sample is the same as the sample in Getting data from Assetic in this article, however the data is returned as a XML string by the inclusion of the additional line specifying XML as the 'Accept' header.
- """
- Example script to get work order using guid.
- Response data is specified as XML rather than the default JSON
- (Assetic.WorkOrderGet_xml.py)
- """
- import assetic
- ##Assetic SDK instance
- asseticsdk = assetic.AsseticSDK(None,None,"Info")
- ##set the response type to XML intead of the default JSON
- asseticsdk.client.default_headers['Accept'] = 'application/xml'
- #instance of work order API
- wkoapi = assetic.WorkOrderApi()
- ##unique ID of work order
- woguid = "115ee90b-7ede-4e00-8ad2-0001f6646bab"
- try:
- wko = wkoapi.work_order_get_0(woguid)
- except assetic.rest.ApiException as e:
- asseticsdk.logger.error("Status {0}, Reason: {1} {2}".format(
- e.status,e.reason,e.body))
- else:
- ##output the response to the console
- print(wko)