From 2f0964dbf759d1bfd3040964634e74a4f8a10ef0 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Sun, 7 Nov 2021 15:46:17 -0500 Subject: [PATCH] 2021-11-07 Fred Gleason * Removed 'xmltodict' Python module dependency to the 'rivwebpyapi' API. Signed-off-by: Fred Gleason --- ChangeLog | 3 + apis/rivwebpyapi/api/rivwebpyapi.py | 182 +++++++++++++++++++++++- apis/rivwebpyapi/tests/Makefile.am | 3 +- apis/rivwebpyapi/tests/list_logs.py | 95 +++++++++++++ apis/rivwebpyapi/tests/list_services.py | 31 ++-- apis/rivwebpyapi/tests/rivwebpyapi.py | 1 - 6 files changed, 296 insertions(+), 19 deletions(-) create mode 100755 apis/rivwebpyapi/tests/list_logs.py delete mode 120000 apis/rivwebpyapi/tests/rivwebpyapi.py diff --git a/ChangeLog b/ChangeLog index e054d4bf..ce7c94fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22578,3 +22578,6 @@ 'rivwebpyapi' API. * Added a 'xmltodict' Python module dependency to the 'rivwebpyapi' API. +2021-11-07 Fred Gleason + * Removed 'xmltodict' Python module dependency to the 'rivwebpyapi' + API. diff --git a/apis/rivwebpyapi/api/rivwebpyapi.py b/apis/rivwebpyapi/api/rivwebpyapi.py index b7eb1d9d..3dcf0425 100755 --- a/apis/rivwebpyapi/api/rivwebpyapi.py +++ b/apis/rivwebpyapi/api/rivwebpyapi.py @@ -18,15 +18,92 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # +import datetime +from datetime import timedelta import requests -import xmltodict +from xml.sax.handler import ContentHandler +import xml.sax + +class RivWebPyApi_ListHandler(ContentHandler): + def __init__(self,base_tag,fields): + self.__output=[] + self.__svc={} + self.__base_tag=base_tag + self.__fields=fields; + self.__field='' + + def output(self): + for d in self.__output: # Convert to the appropriate types + for f in self.__fields.keys(): + d[f]=d[f].strip() + if(self.__fields[f]=='boolean'): + d[f]=d[f]!='0' + elif(self.__fields[f]=='datetime'): + d[f]=self.__parseDatetime(d[f]) + elif(self.__fields[f]=='date'): + d[f]=self.__parseDate(d[f]) + elif(self.__fields[f]=='integer'): + d[f]=int(d[f]) + return self.__output + + def startElement(self,tag,attrs): + if(tag==self.__base_tag): # Create new (empty) record + self.__svc={} + for f in self.__fields.keys(): + self.__svc[f]='' + self.__field=tag + + def endElement(self,tag): + if(tag==self.__base_tag): # Add completed record to output + self.__output.append(self.__svc) + self.__field='' + + def characters(self,content): + if(self.__field in self.__fields.keys()): # Add content to field + self.__svc[self.__field]=self.__svc[self.__field]+content + + def __parseDatetime(self,str): + if(not str): + return None + f0=str.split('T') + if(len(f0)!=2): + raise ValueError('invalid datetime string') + date_parts=f0[0].split('-') + + if(len(date_parts)!=3): + raise ValueError('invalid datetime string') + + time_parts=f0[1][0:7].split(':') + if(len(time_parts)!=3): + raise ValueError('invalid datetime string') + offset_minutes=60*int(f0[1][9:11])+int(f0[1][12:14]) + offset=timedelta(minutes=offset_minutes) + if(f0[1][8:9]=='-'): + offset=-offset + return datetime.datetime(year=int(date_parts[0]), + month=int(date_parts[1]), + day=int(date_parts[2]), + hour=int(time_parts[0]), + minute=int(time_parts[1]), + second=int(time_parts[2]), + tzinfo=datetime.timezone(offset)); + + def __parseDate(self,str): + if(not str): + return None + f0=str.split('-') + if(len(f0)!=3): + raise(ValueError('invalid date string')) + return datetime.date(year=int(f0[0]), + month=int(f0[1]), + day=int(f0[2])); class RivWebPyApi(object): """ Create a 'RivWebPyApi' object for accessing the Web API. """ - def __init__(self,url,username,passwd): + def __init__(self,url,username,password): """ Initialize a WebAPI connection to a Rivendell instance. @@ -42,7 +119,85 @@ class RivWebPyApi(object): """ self.__connection_url=url self.__connection_username=username - self.__connection_password=passwd + self.__connection_password=password + + def ListLogs(self,service_name='',log_name='',trackable=False, + filter_string='',recent=False): + """ + Returns a list of Rivendell logs (dictionary). + + Takes the following arguments: + + service_name - Return only logs belonging to the specified service. + Default is to return all logs. (string) + + log_name - Return only the specified log. Default is to return + all logs. (string) + + trackable - If True, return will include only logs that + contain one or more voice track slots. If False, + all logs will be included. (boolean) + + filter_string - Return only logs that contain the filter_string + in the Name, Description or Service fields. + Default is to return all logs. (string) + + recent - Return the 14 most recently created logs. (boolean) + """ + + # + # Build the WebAPI arguments + # + trackarg='0' + if(trackable): + trackarg='1' + recentarg='0' + if(recent): + recentarg='1' + postdata={ + 'COMMAND': '20', + 'LOGIN_NAME': self.__connection_username, + 'PASSWORD': self.__connection_password, + 'SERVICE_NAME': service_name, + 'LOG_NAME': log_name, + 'TRACKABLE': trackarg, + 'FILTER': filter_string, + 'RECENT': recentarg + } + + # + # Fetch the XML + # + r=requests.post(self.__connection_url,data=postdata) + if(r.status_code!=requests.codes.ok): + r.raise_for_status() + + # + # Generate the output dictionary + # + fields={ + 'name': 'string', + 'serviceName': 'string', + 'description': 'string', + 'originUserName': 'string', + 'originDatetime': 'datetime', + 'linkDatetime': 'datetime', + 'modifiedDatetime': 'datetime', + 'purgeDate': 'date', + 'autoRefresh': 'boolean', + 'startDate': 'date', + 'endDate': 'date', + 'scheduledTracks': 'integer', + 'completedTracks': 'integer', + 'musicLinks': 'integer', + 'musicLinked': 'boolean', + 'trafficLinks': 'integer', + 'trafficLinked': 'boolean' + } + handler=RivWebPyApi_ListHandler(base_tag='log',fields=fields) + xml.sax.parseString(r.text,handler) + + return handler.output() def ListServices(self,trackable): """ @@ -54,6 +209,10 @@ class RivWebPyApi(object): are configured to support voice tracking. If False, all configured services will be included. """ + + # + # Build the WebAPI arguments + # trackarg='0' if(trackable): trackarg='1' @@ -63,7 +222,22 @@ class RivWebPyApi(object): 'PASSWORD': self.__connection_password, 'TRACKABLE': trackarg } + + # + # Fetch the XML + # r=requests.post(self.__connection_url,data=postdata) if(r.status_code!=requests.codes.ok): r.raise_for_status() - return xmltodict.parse(r.text) + + # + # Generate the output dictionary + # + fields={ + 'name': 'string', + 'description': 'string' + } + handler=RivWebPyApi_ListHandler(base_tag='service',fields=fields) + xml.sax.parseString(r.text,handler) + + return handler.output() diff --git a/apis/rivwebpyapi/tests/Makefile.am b/apis/rivwebpyapi/tests/Makefile.am index a8b36c46..67f05fcf 100644 --- a/apis/rivwebpyapi/tests/Makefile.am +++ b/apis/rivwebpyapi/tests/Makefile.am @@ -20,7 +20,8 @@ ## ## Use automake to process this into a Makefile.in -EXTRA_DIST = list_services.py +EXTRA_DIST = list_logs.py\ + list_services.py CLEANFILES = *~\ *.idb\ diff --git a/apis/rivwebpyapi/tests/list_logs.py b/apis/rivwebpyapi/tests/list_logs.py new file mode 100755 index 00000000..d19ee250 --- /dev/null +++ b/apis/rivwebpyapi/tests/list_logs.py @@ -0,0 +1,95 @@ +#!%PYTHON_BANGPATH% + +# list_logs.py +# +# RivWebPyApi test script for Rivendell +# +# Test the ListLogs Web API call +# +# (C) Copyright 2021 Fred Gleason +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import getpass +import rivwebpyapi +import sys + +url=''; +username='' +password='' +service_name='' +log_name='' +trackable=False +filter_string='' +recent=False + +# +# Get login parameters +# +usage='list_logs --url= --username= [--password=]' +for arg in sys.argv: + f0=arg.split('=') + if(len(f0)==2): + if(f0[0]=='--url'): + url=f0[1] + if(f0[0]=='--username'): + username=f0[1] + if(f0[0]=='--password'): + password=f0[1] + if(f0[0]=='--service-name'): + service_name=f0[1] + if(f0[0]=='--log-name'): + log_name=f0[1] + if(f0[0]=='--trackable'): + trackable=True + if(f0[0]=='--filter-string'): + filter_string=f0[1] + if(f0[0]=='--recent'): + recent=True +if(not password): + password=getpass.getpass() +if((not url)or(not username)): + print(usage) + sys.exit(1) + +# +# Get the log list +# +webapi=rivwebpyapi.RivWebPyApi(url=url,username=username,password=password) +logs=webapi.ListLogs(service_name=service_name,log_name=log_name, + trackable=trackable,filter_string=filter_string, + recent=recent) + +# +# Display the log list +# +for log in logs: + print('name: '+log['name']) + print('serviceName: '+log['serviceName']) + print('description: '+log['description']) + print('originUserName: '+log['originUserName']) + print('originDatetime: '+str(log['originDatetime'])) + print('linkDatetime: '+str(log['linkDatetime'])) + print('modifiedDatetime: '+str(log['modifiedDatetime'])) + print('startDate: '+str(log['startDate'])) + print('endDate: '+str(log['endDate'])) + print('purgeDate: '+str(log['purgeDate'])) + print('scheduledTracks: '+str(log['scheduledTracks'])) + print('completedTracks: '+str(log['completedTracks'])) + print('musicLinks: '+str(log['musicLinks'])) + print('musicLinked: '+str(log['musicLinked'])) + print('trafficLinks: '+str(log['trafficLinks'])) + print('trafficLinked: '+str(log['trafficLinked'])) + print('') diff --git a/apis/rivwebpyapi/tests/list_services.py b/apis/rivwebpyapi/tests/list_services.py index ab1eb9ed..e2ff2a3e 100755 --- a/apis/rivwebpyapi/tests/list_services.py +++ b/apis/rivwebpyapi/tests/list_services.py @@ -26,14 +26,14 @@ import getpass import rivwebpyapi import sys -url=''; -username='' -password='' - # # Get login parameters # -usage='login_test --url= --username=' +usage='list_services --url= --username= [--trackable]' +url=''; +username='' +password='' +trackable=False for arg in sys.argv: f0=arg.split('=') if(len(f0)==2): @@ -43,19 +43,24 @@ for arg in sys.argv: username=f0[1] if(f0[0]=='--password'): password=f0[1] + if(f0[0]=='--trackable'): + trackable=True if(not password): password=getpass.getpass() if((not url)or(not username)): print(usage) sys.exit(1) +# +# Get the services list +# +webapi=rivwebpyapi.RivWebPyApi(url=url,username=username,password=password) +services=webapi.ListServices(trackable=trackable) -webapi=rivwebpyapi.RivWebPyApi(url=url,username=username,passwd=password) -services=webapi.ListServices(False) -for svc in services['serviceList']['service']: - print('NAME: '+svc['name']) - if(svc['description']): - print('DESCRIPTION: '+svc['description']) - else: - print('DESCRIPTION: [none]') +# +# Display the services list +# +for svc in services: + print('name: '+svc['name']) + print('description: '+svc['description']) print('') diff --git a/apis/rivwebpyapi/tests/rivwebpyapi.py b/apis/rivwebpyapi/tests/rivwebpyapi.py deleted file mode 120000 index 0ea3f162..00000000 --- a/apis/rivwebpyapi/tests/rivwebpyapi.py +++ /dev/null @@ -1 +0,0 @@ -../api/rivwebpyapi.py \ No newline at end of file