<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Juletip #9: Calling SAS program from Python using REST API in SAS Community Nordic</title>
    <link>https://communities.sas.com/t5/SAS-Community-Nordic/Juletip-9-Calling-SAS-program-from-Python-using-REST-API/m-p/848686#M397</link>
    <description>&lt;P&gt;&lt;SPAN&gt;I hear quite often nowadays this question: how do I run my SAS (Viya) programs from outside using REST APIs?&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;There is an excellent Github repository &lt;/SPAN&gt;which addresses this question, &lt;SPAN&gt;with several practical code examples:&amp;nbsp;&lt;A href="https://github.com/sassoftware/rest-api-use-cases" target="_self"&gt;https://github.com/sassoftware/rest-api-use-cases&lt;/A&gt;&amp;nbsp;.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;By following these examples, I tested SAS job execution from Python client. My example SAS program produces some summary statistics and a line graph from SASHELP.AIR dataset,&lt;/SPAN&gt; &lt;SPAN&gt;which is filtered by two macro variables.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=""&gt;/* Create dataset */&lt;BR /&gt;proc sql;
   create table air_subset as	
   select * from sashelp.air
   where date between "&amp;amp;startdate"d and "&amp;amp;enddate"d;
quit;


ods graphics / reset width=6.4in height=4.8in imagemap;

/* Summary statistics */&lt;BR /&gt;proc means data=WORK.AIR_SUBSET chartype mean std min max median n vardef=df qmethod=os;
    var AIR;
run;

/* Histogram plot */&lt;BR /&gt;proc univariate data=WORK.AIR_SUBSET vardef=df noprint;
    var AIR;
    histogram AIR;
run;

proc sort data=WORK.AIR_SUBSET out=_SeriesPlotTaskData;
    by DATE;
run;

/* Line graph*/&lt;BR /&gt;proc sgplot data=_SeriesPlotTaskData;
    series x=DATE y=AIR /;
    xaxis grid;
    yaxis grid;
run;
ods graphics / reset;

proc datasets library=WORK noprint;
   delete _SeriesPlotTaskData;
run;&lt;/CODE&gt;&lt;/PRE&gt;&lt;P&gt;&lt;SPAN&gt;I saved the sas-program as plot_air.sas an&lt;/SPAN&gt;d&lt;STRONG&gt; created a job definition&lt;/STRONG&gt; of it. I placed the job definition in Public-folder.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="askojuvonen1_0-1670516509106.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/78196iE913012F4143EBC8/image-size/medium?v=v2&amp;amp;px=400" role="button" title="askojuvonen1_0-1670516509106.png" alt="askojuvonen1_0-1670516509106.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Our SAS part of the experiment is now ready!&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Let’s do the REST call in Python next.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;First thing to do is the authentication. For my testing purposes, I authenticate with user id and password. There are more secure ways to do the authentication: you should follow the security policies of your organization.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Note that steps 1 and 2 are for registrating the client, and they are not needed to run after initial registration.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;import requests as request
import time

# To get the client_token, you need to run this command in Viya server: 
# kubectl -n sse get secret sas-consul-client -o jsonpath="{.data.CONSUL_TOKEN}" | echo "$(base64 -d)"

sasserver = "https://myviyaserver.sas.com"
client_token = "&amp;lt;client token from kubectl command above&amp;gt;"
username =  “myusername”
password = “mypassword”
client_id = "myclient"
client_secret = "mysecret"&lt;BR /&gt;
# Step 1 : Get the client access token
url = sasserver+"/SASLogon/oauth/clients/consul?callback=false&amp;amp;serviceId=" + client_id
payload = {}
headers = {
  "X-Consul-Token": client_token
}
response = request.request("POST", url, headers=headers, data = payload, verify=False).json()
client_access_token = response["access_token"]&lt;BR /&gt;
# Step 2 : Register the client 
url = sasserver+"/SASLogon/oauth/clients"
payload = {"client_id": client_id, 
           "client_secret": client_secret,
           "scope": ["*"], 
           "resource_ids": "none", 
           "authorities": ["uaa.none"], 
           "authorized_grant_types": ["password"],
           "access_token_validity": 36000}

headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer ' + client_access_token
}
response = request.request("POST", url, headers = headers, json = payload, verify=False).json()

# Step 3 : Get the access token
url = sasserver + "/SASLogon/oauth/token"
data = {
    'grant_type': 'password',
    'username': username,
    'password': password
}

headers = {'Accept': 'application/json'}

response = request.post(url, headers=headers, data=data, auth=(client_id, client_secret), verify=False).json()
access_token = response["access_token"]

&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Step 4 makes the REST API call to the desired job (/Public/plot_air). Note that the macro variables (startdate, enddate) which filter the AIR-dataset&amp;nbsp; in plot_air.sas are set as parameters in URL.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;# Step 4 : Call the SAS job plot_air with parameters startdate and enddate
job_url = sasserver+"/SASJobExecution/?_program=/Public/plot_air&amp;amp;startdate=1JAN1950&amp;amp;enddate=31DEC1959"
url = job_url + '&amp;amp;_action=json&amp;amp;_resultfile=*&amp;amp;_omittextlog=false'

headers = {
    'Authorization': 'bearer ' + access_token,
    'Content-Type': 'application/vnd.sas.job.execution.job.request',
    'Accept': 'application/vnd.sas.job.execution.job'
}

r = request.post(url, headers=headers, verify=False).json()&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;In Step 5, the result URLs are read from the HTTP response. &lt;STRONG&gt;log_uri &lt;/STRONG&gt;contains the URL into SAS log, and &lt;STRONG&gt;output_uri&lt;/STRONG&gt;&amp;nbsp; into SAS output.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;# Step 5 : get the SAS log and SAS output URLs
log_uri = sasserver + r['items'][1]['href']
output_uri = sasserver + r['items'][2]['href']
print(log_uri)
print(output_uri)&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;In the last step, the HTML result is written into local file.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;# Step 6 : write the SAS output html-file into local folder

output_str = request.get(output_uri, headers=headers, verify=False).text
filename = "c:\\temp\\Output_"+time.strftime("%Y%m%d-%H%M%S")+".html"
with open(filename, "w") as output_html:
    output_html.write(output_str)&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;And this is how the result HTML looks!&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="askojuvonen1_0-1670518011444.png" style="width: 999px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/78198iB2E9BE079AC7AFB2/image-size/large?v=v2&amp;amp;px=999" role="button" title="askojuvonen1_0-1670518011444.png" alt="askojuvonen1_0-1670518011444.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Fri, 09 Dec 2022 06:38:15 GMT</pubDate>
    <dc:creator>askojuvonen1</dc:creator>
    <dc:date>2022-12-09T06:38:15Z</dc:date>
    <item>
      <title>Juletip #9: Calling SAS program from Python using REST API</title>
      <link>https://communities.sas.com/t5/SAS-Community-Nordic/Juletip-9-Calling-SAS-program-from-Python-using-REST-API/m-p/848686#M397</link>
      <description>&lt;P&gt;&lt;SPAN&gt;I hear quite often nowadays this question: how do I run my SAS (Viya) programs from outside using REST APIs?&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;There is an excellent Github repository &lt;/SPAN&gt;which addresses this question, &lt;SPAN&gt;with several practical code examples:&amp;nbsp;&lt;A href="https://github.com/sassoftware/rest-api-use-cases" target="_self"&gt;https://github.com/sassoftware/rest-api-use-cases&lt;/A&gt;&amp;nbsp;.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;By following these examples, I tested SAS job execution from Python client. My example SAS program produces some summary statistics and a line graph from SASHELP.AIR dataset,&lt;/SPAN&gt; &lt;SPAN&gt;which is filtered by two macro variables.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=""&gt;/* Create dataset */&lt;BR /&gt;proc sql;
   create table air_subset as	
   select * from sashelp.air
   where date between "&amp;amp;startdate"d and "&amp;amp;enddate"d;
quit;


ods graphics / reset width=6.4in height=4.8in imagemap;

/* Summary statistics */&lt;BR /&gt;proc means data=WORK.AIR_SUBSET chartype mean std min max median n vardef=df qmethod=os;
    var AIR;
run;

/* Histogram plot */&lt;BR /&gt;proc univariate data=WORK.AIR_SUBSET vardef=df noprint;
    var AIR;
    histogram AIR;
run;

proc sort data=WORK.AIR_SUBSET out=_SeriesPlotTaskData;
    by DATE;
run;

/* Line graph*/&lt;BR /&gt;proc sgplot data=_SeriesPlotTaskData;
    series x=DATE y=AIR /;
    xaxis grid;
    yaxis grid;
run;
ods graphics / reset;

proc datasets library=WORK noprint;
   delete _SeriesPlotTaskData;
run;&lt;/CODE&gt;&lt;/PRE&gt;&lt;P&gt;&lt;SPAN&gt;I saved the sas-program as plot_air.sas an&lt;/SPAN&gt;d&lt;STRONG&gt; created a job definition&lt;/STRONG&gt; of it. I placed the job definition in Public-folder.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="askojuvonen1_0-1670516509106.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/78196iE913012F4143EBC8/image-size/medium?v=v2&amp;amp;px=400" role="button" title="askojuvonen1_0-1670516509106.png" alt="askojuvonen1_0-1670516509106.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Our SAS part of the experiment is now ready!&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Let’s do the REST call in Python next.&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;First thing to do is the authentication. For my testing purposes, I authenticate with user id and password. There are more secure ways to do the authentication: you should follow the security policies of your organization.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Note that steps 1 and 2 are for registrating the client, and they are not needed to run after initial registration.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;import requests as request
import time

# To get the client_token, you need to run this command in Viya server: 
# kubectl -n sse get secret sas-consul-client -o jsonpath="{.data.CONSUL_TOKEN}" | echo "$(base64 -d)"

sasserver = "https://myviyaserver.sas.com"
client_token = "&amp;lt;client token from kubectl command above&amp;gt;"
username =  “myusername”
password = “mypassword”
client_id = "myclient"
client_secret = "mysecret"&lt;BR /&gt;
# Step 1 : Get the client access token
url = sasserver+"/SASLogon/oauth/clients/consul?callback=false&amp;amp;serviceId=" + client_id
payload = {}
headers = {
  "X-Consul-Token": client_token
}
response = request.request("POST", url, headers=headers, data = payload, verify=False).json()
client_access_token = response["access_token"]&lt;BR /&gt;
# Step 2 : Register the client 
url = sasserver+"/SASLogon/oauth/clients"
payload = {"client_id": client_id, 
           "client_secret": client_secret,
           "scope": ["*"], 
           "resource_ids": "none", 
           "authorities": ["uaa.none"], 
           "authorized_grant_types": ["password"],
           "access_token_validity": 36000}

headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer ' + client_access_token
}
response = request.request("POST", url, headers = headers, json = payload, verify=False).json()

# Step 3 : Get the access token
url = sasserver + "/SASLogon/oauth/token"
data = {
    'grant_type': 'password',
    'username': username,
    'password': password
}

headers = {'Accept': 'application/json'}

response = request.post(url, headers=headers, data=data, auth=(client_id, client_secret), verify=False).json()
access_token = response["access_token"]

&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Step 4 makes the REST API call to the desired job (/Public/plot_air). Note that the macro variables (startdate, enddate) which filter the AIR-dataset&amp;nbsp; in plot_air.sas are set as parameters in URL.&amp;nbsp;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;# Step 4 : Call the SAS job plot_air with parameters startdate and enddate
job_url = sasserver+"/SASJobExecution/?_program=/Public/plot_air&amp;amp;startdate=1JAN1950&amp;amp;enddate=31DEC1959"
url = job_url + '&amp;amp;_action=json&amp;amp;_resultfile=*&amp;amp;_omittextlog=false'

headers = {
    'Authorization': 'bearer ' + access_token,
    'Content-Type': 'application/vnd.sas.job.execution.job.request',
    'Accept': 'application/vnd.sas.job.execution.job'
}

r = request.post(url, headers=headers, verify=False).json()&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;In Step 5, the result URLs are read from the HTTP response. &lt;STRONG&gt;log_uri &lt;/STRONG&gt;contains the URL into SAS log, and &lt;STRONG&gt;output_uri&lt;/STRONG&gt;&amp;nbsp; into SAS output.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;# Step 5 : get the SAS log and SAS output URLs
log_uri = sasserver + r['items'][1]['href']
output_uri = sasserver + r['items'][2]['href']
print(log_uri)
print(output_uri)&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;In the last step, the HTML result is written into local file.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;# Step 6 : write the SAS output html-file into local folder

output_str = request.get(output_uri, headers=headers, verify=False).text
filename = "c:\\temp\\Output_"+time.strftime("%Y%m%d-%H%M%S")+".html"
with open(filename, "w") as output_html:
    output_html.write(output_str)&lt;/PRE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;And this is how the result HTML looks!&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="askojuvonen1_0-1670518011444.png" style="width: 999px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/78198iB2E9BE079AC7AFB2/image-size/large?v=v2&amp;amp;px=999" role="button" title="askojuvonen1_0-1670518011444.png" alt="askojuvonen1_0-1670518011444.png" /&gt;&lt;/span&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 09 Dec 2022 06:38:15 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Community-Nordic/Juletip-9-Calling-SAS-program-from-Python-using-REST-API/m-p/848686#M397</guid>
      <dc:creator>askojuvonen1</dc:creator>
      <dc:date>2022-12-09T06:38:15Z</dc:date>
    </item>
  </channel>
</rss>

