#!/usr/bin/env python3 ''' ADI builder author: Steven Kuterna steven.kuterna@devoteam.com ''' import sys import argparse import logging import os import shutil import yaml from lxml import etree as ET import hashlib from datetime import datetime loglevel= logging.WARNING logger = logging.getLogger() logger.setLevel(loglevel) ch = logging.StreamHandler() ch.setLevel(loglevel) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) def insertMetadata(tree, config): if config['Verb'] is None: config['Verb'] = '' if config['CreateDate'] is None: now = datetime.now() date = now.strftime("%Y-%m-%d") config['CreateDate'] = date try: attributes= { 'Asset_Class': 'package', 'Asset_ID': str(config['AssetId']), 'Asset_Name': str(config['AssetName']), 'Provider': str(config['Provider']), 'Provider_ID': str(config['ProviderId']), 'Product': 'MOD', 'Description': str(config['Description']), 'Creation_Date': str(config['CreateDate']), 'Verb': str(config['Verb']), 'Version_Major': str(config['VersionMajor']), 'Version_Minor': str(config['VersionMinor']) } except KeyError as e: logger.error('failed to add Metadata AMS attribute '+str(e)) sys.exit(0) ET.SubElement(tree, 'AMS', attributes) def createAMSattributes(asset_type, metadata): if metadata['Verb'] is None: metadata['Verb'] = '' try: ams_attributes= { 'Asset_Class': asset_type, 'Asset_ID': asset_type.upper() + str(metadata['AssetId']), 'Asset_Name': str(metadata['AssetName'] + ' ' + asset_type), 'Provider': str(metadata['Provider']), 'Provider_ID': str(metadata['ProviderId']), 'Product': 'MOD', 'Description': str(metadata['Description'] + ' ' + asset_type), 'Creation_Date': str(metadata['CreateDate']), 'Verb': str(metadata['Verb']), 'Version_Major': str(metadata['VersionMajor']), 'Version_Minor': str(metadata['VersionMinor']) } except KeyError as e: logger.error('failed to add Metadata AMS attribute '+str(e)) sys.exit(0) return ams_attributes def addAsset(tree, asset_type, asset, metadata, output): if not 'Content' in asset: for subasset in asset: addAsset(tree, asset_type, asset[subasset], metadata, output) return logger.info(f'insert asset: {asset}') ams_attributes = createAMSattributes(asset_type, metadata) assetTree = ET.SubElement(tree, 'Asset') assetMetadata = ET.SubElement(assetTree, 'Metadata') ET.SubElement(assetMetadata, 'AMS', ams_attributes) ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': 'Type', 'Value': asset_type}) for element in asset: if type(asset[element]) is dict: for subelement in asset[element]: if type(asset[element][subelement]) is dict: for subsubelemment in asset[element][subelement]: ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': subsubelemment, 'Value': str(asset[element][subelement][subsubelemment])}) else: if asset[element][subelement] is None: asset[element][subelement] = '' ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': str(asset[element][subelement])}) else: # is it content? add it under the Asset level if str(element).upper() == 'CONTENT_CHECKSUM' or str(element).upper() == 'CONTENT_FILESIZE': continue if str(element).upper() == 'CONTENT': if not os.path.exists(args.input): logger.error(f"content file {asset[element]} is not found, exiting...") sys.exit(0) ET.SubElement(assetTree, 'Content', {'Value': asset[element]}) checksum = hashlib.md5(open(asset[element],'rb').read()).hexdigest() filesize = os.path.getsize(asset[element]) ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': 'Content_CheckSum', 'Value': str(checksum)}) ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': 'Content_Filesize', 'Value': str(filesize)}) shutil.copyfile(asset[element], f'{output}/{asset[element]}') continue # no content, add it under the Metadata level if asset[element] is None: asset[element] = '' ET.SubElement(assetMetadata, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': str(asset[element])}) def addTitle(tree, title, metadata): logger.info(f'insert title: {title}') ams_attributes = createAMSattributes('title', metadata) titleTree = ET.SubElement(tree, 'Metadata') ET.SubElement(titleTree, 'AMS', ams_attributes) ET.SubElement(titleTree, 'App_Data', {'App': 'MOD', 'Name': 'Type', 'Value': 'title'}) for element in title: # check if there are multiple elements available if type(title[element]) == dict: for subelement in title[element]: # check if it contains multiple attributes like Language and a Value if type(title[element][subelement]) == dict: attributes = {'App': 'MOD', 'Name': element} # if the attribute is the same as the element change it to 'Value' for subsubelement in title[element][subelement]: if subsubelement == element: attributes.update({'Value': title[element][subelement][subsubelement]}) else: # just add it to the attributes list attributes.update({subsubelement: title[element][subelement][subsubelement]}) # add the multiattribute element to the tree ET.SubElement(titleTree, 'App_Data', attributes) continue # single attribute, add to the tree if title[element][subelement] is None: title[element][subelement] = '' ET.SubElement(titleTree, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': title[element][subelement]}) else: # single element, add to the tree if title[element] is None: title[element] = '' ET.SubElement(titleTree, 'App_Data', {'App': 'MOD', 'Name': element, 'Value': str(title[element])}) def insertAssets(tree, metadata, assets, output): for asset in assets: if asset.upper() == 'TITLE': addTitle(tree, assets[asset], metadata) else: addAsset(tree, asset, assets[asset], metadata, output) return if __name__ == "__main__" : argparser = argparse.ArgumentParser(description='Creates ADI from yaml...') argparser.add_argument('--input', required=False, default="adi.yaml", help="yaml input to build adi.xml") argparser.add_argument('--output', required=True, help="output directory for xml and content files") args = argparser.parse_args() logger.debug(f"generating adi based on {args.input}") if not os.path.exists(args.input): logger.error(f"file {args.input} is not found, exiting...") sys.exit(0) with open(args.input, 'r') as ymlfile: cfg = yaml.load(ymlfile, Loader=yaml.FullLoader) try: os.mkdir(args.output) except FileExistsError: logger.warning(f'directory {args.output} already exists!') root = ET.Element('ADI') metadataTree = ET.SubElement(root, 'Metadata') insertMetadata(metadataTree, cfg['metadata']) assetTree = ET.SubElement(root, 'Asset') insertAssets(assetTree, cfg['metadata'], cfg['asset'], args.output) tree = ET.ElementTree(root) try: tree.write(f'{args.output}/adi.xml', encoding='utf-8', xml_declaration=True, pretty_print=True, doctype='') except Exception as e: print(str(e))