#!/usr/bin/env python """ Generates fake XML for asset metadata. """ import random from datetime import datetime, timedelta from lxml import etree from opaque_keys.edx.keys import CourseKey from xmodule.assetstore import AssetMetadata try: import click except ImportError: click = None # Name of the asset metadata XML schema definition file. ASSET_XSD_FILE = 'assets.xsd' # Characters used in name generation below. NAME_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-' NAME_CHARS_W_UNICODE = NAME_CHARS + 'àĚŘDžΦШΩΣӔ' def coin_flip(): """ 50/50 chance """ return random.choice((True, False)) def asset_type(): """ Pick an asset type at random. """ asset_type_choices = ( (95, "asset"), (100, "video") ) d100 = random.randint(0, 100) for choice in asset_type_choices: if d100 <= choice[0]: return choice[1] return asset_type_choices[-1][1] def filename(): """ Fake a filename. """ fname = '' for __ in range(random.randint(10, 30)): fname += random.choice(NAME_CHARS_W_UNICODE) fname += random.choice(('.jpg', '.pdf', '.png', '.txt')) return fname def pathname(): """ Fake a pathname. """ pname = '' for __ in range(random.randint(2, 3)): for __ in range(random.randint(5, 10)): pname += random.choice(NAME_CHARS) pname += '/' return pname def locked(): """ Locked or unlocked. """ return coin_flip() def fields(): """ Generate some fake extra fields. """ f = {} if coin_flip(): if coin_flip(): f['copyrighted'] = coin_flip() if coin_flip(): f['size'] = random.randint(100, 10000000) if coin_flip(): f['color'] = random.choice(('blue', 'pink', 'fuchsia', 'rose', 'mauve', 'black')) return f def user_id(): """ Fake user id. """ return random.randint(1, 100000000) def versions(): """ Fake versions. """ curr_ver = random.randint(1, 500) prev_ver = curr_ver - 1 def ver_str(ver): """ Version string. """ return f'v{ver}.0' return (ver_str(curr_ver), ver_str(prev_ver)) def date_and_time(): """ Fake date/time. """ start_date = datetime.now() time_back = timedelta(seconds=random.randint(0, 473040000)) # 15 year interval return start_date - time_back def contenttype(): """ Random MIME type. """ return random.choice(( 'image/jpeg', 'text/html', 'audio/aiff', 'video/avi', 'text/plain', 'application/msword', 'application/x-gzip', 'application/javascript', )) def generate_random_asset_md(): """ Generates a single AssetMetadata object with semi-random data. """ course_key = CourseKey.from_string('org/course/run') asset_key = course_key.make_asset_key(asset_type(), filename()) (curr_version, prev_version) = versions() return AssetMetadata( asset_key, pathname=pathname(), internal_name=str([filename() for __ in range(10)]), locked=locked(), contenttype=contenttype(), thumbnail=filename(), fields=fields(), curr_version=curr_version, prev_version=prev_version, edited_by=user_id(), edited_by_email='staff@edx.org', edited_on=date_and_time(), created_by=user_id(), created_by_email='staff@edx.org', created_on=date_and_time(), ) def make_asset_md(amount): """ Make a number of fake AssetMetadata objects. """ all_asset_md = [] for __ in range(amount): all_asset_md.append(generate_random_asset_md()) return all_asset_md def make_asset_xml(amount, xml_filename): """ Make an XML file filled with fake AssetMetadata. """ all_md = make_asset_md(amount) xml_root = etree.Element("assets") for mdata in all_md: asset_element = etree.SubElement(xml_root, "asset") mdata.to_xml(asset_element) with open(xml_filename, "w") as xml_file: etree.ElementTree(xml_root).write(xml_file) def validate_xml(xsd_filename, xml_filename): """ Validate a generated XML file against the XSD. """ with open(xsd_filename) as f: schema_root = etree.XML(f.read()) schema = etree.XMLSchema(schema_root) xmlparser = etree.XMLParser(schema=schema) with open(xml_filename) as f: etree.fromstring(f.read(), xmlparser) if click is not None: @click.command() @click.option('--num_assets', type=click.INT, default=10, help="Number of assets to be generated by the script.", required=False ) @click.option('--output_xml', type=click.File('w'), default=AssetMetadata.EXPORTED_ASSET_FILENAME, help="Filename for the output XML file.", required=False ) @click.option('--input_xsd', type=click.File('r'), default=ASSET_XSD_FILE, help="Filename for the XSD (schema) file to read in.", required=False ) def cli(num_assets, output_xml, input_xsd): """ Generates a number of fake asset metadata items as XML - and validates the XML against the schema. """ make_asset_xml(num_assets, output_xml) # Now - validate the XML against the XSD. validate_xml(input_xsd, output_xml) if __name__ == '__main__': if click is not None: cli() # pylint: disable=no-value-for-parameter else: print("Aborted! Module 'click' is not installed.")