From 68dee3f3a451c8bec99c7b76fcbf6a5a4d67a5c0 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 10 Mar 2018 13:10:20 +0000 Subject: [PATCH] Catalog support for multiple author tags This will enable richer author meta-data in aircraft, and hence nicer GUI presentation of author information. Client-side support still to be added before this can be used --- catalog/catalog.py | 59 +++++++++++++++++-- catalog/testData/Aircraft/c172/c172-set.xml | 9 +++ catalog/testData/Aircraft/f16/f16-common.xml | 13 +++- catalog/testData/Aircraft/f16/f16b-set.xml | 9 ++- catalog/test_catalog.py | 62 +++++++++++++++++--- 5 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 catalog/testData/Aircraft/c172/c172-set.xml diff --git a/catalog/catalog.py b/catalog/catalog.py index 777520d..9540244 100644 --- a/catalog/catalog.py +++ b/catalog/catalog.py @@ -10,6 +10,16 @@ import sys import catalogTags CATALOG_VERSION = 4 +quiet = False +verbose = False + +def warning(msg): + if not quiet: + print(msg) + +def log(msg): + if verbose: + print(msg) # xml node (robust) get text helper def get_xml_text(e): @@ -43,13 +53,17 @@ def scan_set_file(aircraft_dir, set_file, includes): variant = {} name = sim_node.getValue("description", None) if (name == None or len(name) == 0): - print "Set file " + set_file + " is missing a , skipping" + warning("Set file " + set_file + " is missing a , skipping") return None variant['name'] = name variant['status'] = sim_node.getValue("status", None) - if sim_node.hasChild('author'): + if sim_node.hasChild('authors'): + # aircraft has structured authors data, handle that + variant['authors'] = extract_authors(sim_node.getChild('authors')) + + elif sim_node.hasChild('author'): variant['author'] = sim_node.getValue("author", None) if sim_node.hasChild('long-description'): @@ -94,7 +108,7 @@ def extract_previews(previews_node, aircraft_dir): # check path exists in base-name-dir fullPath = os.path.join(aircraft_dir, previewPath) if not os.path.isfile(fullPath): - print "Bad preview path, skipping:" + fullPath + warning("Bad preview path, skipping:" + fullPath) continue result.append({'type':previewType, 'path':previewPath}) @@ -106,11 +120,25 @@ def extract_tags(tags_node, set_path): tag = node.value # check tag is in the allowed list if not catalogTags.isValidTag(tag): - print "Unknown tag value:", tag, " in ", set_path + warning("Unknown tag value:" + tag + " in " + set_path) result.append(tag) return result +def extract_authors(authors_node): + result = [] + for author in authors_node.getChildren("author"): + authorName = author.getValue("name", None) + if (authorName == None): + continue + + authorNick = author.getValue("nick", None) + authorEmail = author.getValue("email", None) + authorDesc = author.getValue("description", None) + + result.append({'name':authorName, 'nick':authorNick, 'email':authorEmail, 'description':authorDesc}) + return result + # scan all the -set.xml files in an aircraft directory. Returns a # package dict and a list of variants. def scan_aircraft_dir(aircraft_dir, includes): @@ -187,6 +215,25 @@ def append_tag_nodes(node, variant): for tag in variant['tags']: node.append(make_xml_leaf('tag', tag)) +def append_author_nodes(node, info): + if 'authors' in info: + authors_node = ET.Element('authors') + for a in info['authors']: + a_node = ET.Element('author') + a_node.append(make_xml_leaf('name', a['name'])) + if (a['email'] != None): + a_node.append(make_xml_leaf('email', a['email'])) + if (a['nick'] != None): + a_node.append(make_xml_leaf('nick', a['nick'])) + if (a['description'] != None): + a_node.append(make_xml_leaf('description', a['description'])) + authors_node.append(a_node) + + node.append(authors_node) + elif 'author' in info: + # traditional single author string + node.append( make_xml_leaf('author', info['author']) ) + def make_aircraft_node(aircraftDirName, package, variants, downloadBase): #print "package:", package #print "variants:", variants @@ -194,8 +241,7 @@ def make_aircraft_node(aircraftDirName, package, variants, downloadBase): package_node.append( make_xml_leaf('name', package['name']) ) package_node.append( make_xml_leaf('status', package['status']) ) - if 'author' in package: - package_node.append( make_xml_leaf('author', package['author']) ) + append_author_nodes(package_node, package) if 'description' in package: package_node.append( make_xml_leaf('description', package['description']) ) @@ -242,6 +288,7 @@ def make_aircraft_node(aircraftDirName, package, variants, downloadBase): append_preview_nodes(variant_node, variant, downloadBase, aircraftDirName) append_tag_nodes(variant_node, variant) + append_author_nodes(variant_node, variant) package_node.append( make_xml_leaf('dir', aircraftDirName) ) diff --git a/catalog/testData/Aircraft/c172/c172-set.xml b/catalog/testData/Aircraft/c172/c172-set.xml new file mode 100644 index 0000000..e7d28ad --- /dev/null +++ b/catalog/testData/Aircraft/c172/c172-set.xml @@ -0,0 +1,9 @@ + + + + c172 + Cessna 172P + Wilbur Wright + + + diff --git a/catalog/testData/Aircraft/f16/f16-common.xml b/catalog/testData/Aircraft/f16/f16-common.xml index def9da9..840832d 100644 --- a/catalog/testData/Aircraft/f16/f16-common.xml +++ b/catalog/testData/Aircraft/f16/f16-common.xml @@ -1,7 +1,18 @@ - Wilbur Wright + + + Wilbur Wright + ww@wright.com + wilburw + Model, FDM and cockpit + + + Orville Wright + Testing and systems + + fighter 1980s diff --git a/catalog/testData/Aircraft/f16/f16b-set.xml b/catalog/testData/Aircraft/f16/f16b-set.xml index 01d8fc1..3379f86 100644 --- a/catalog/testData/Aircraft/f16/f16b-set.xml +++ b/catalog/testData/Aircraft/f16/f16b-set.xml @@ -6,7 +6,14 @@ The F16-B is an upgraded version of the F16A. f16a - James T Kirk + + + James T Kirk + shatner@enterprise.com + starlover + Everything + + diff --git a/catalog/test_catalog.py b/catalog/test_catalog.py index 00e7a22..ed0f669 100755 --- a/catalog/test_catalog.py +++ b/catalog/test_catalog.py @@ -6,6 +6,8 @@ import os import catalog import lxml.etree as ET +catalog.quiet = True + class UpdateCatalogTests(unittest.TestCase): def test_scan_set(self): info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"]) @@ -13,18 +15,30 @@ class UpdateCatalogTests(unittest.TestCase): self.assertEqual(info['name'], 'F16-A') self.assertEqual(info['primary-set'], True) self.assertEqual(info['variant-of'], None) - self.assertEqual(info['author'], 'Wilbur Wright') self.assertEqual(info['rating_FDM'], 3) self.assertEqual(info['rating_model'], 5) self.assertEqual(len(info['tags']), 3) self.assertEqual(info['minimum-fg-version'], '2017.4') + authorsArray = info['authors'] + self.assertNotIn('author', info) + self.assertEqual(len(authorsArray), 2) + + author0 = authorsArray[0] + self.assertEqual(author0['name'], 'Wilbur Wright') + self.assertEqual(author0['nick'], 'wilburw') + self.assertEqual(author0['email'], 'ww@wright.com') + + author1 = authorsArray[1] + self.assertEqual(author1['name'], 'Orville Wright') + # self.assertNotIn('nick', author1) + # self.assertNotIn('email', author1) + def test_scan_dir(self): (pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/f16", ["testData/OtherDir"]) self.assertEqual(pkg['id'], 'f16a') f16trainer = next(v for v in variants if v['id'] == 'f16-trainer') - self.assertEqual(pkg['author'], 'Wilbur Wright') self.assertEqual(len(variants), 3) self.assertEqual(pkg['minimum-fg-version'], '2017.4') @@ -38,14 +52,29 @@ class UpdateCatalogTests(unittest.TestCase): f16b = next(v for v in variants if v['id'] == 'f16b') self.assertEqual(f16b['variant-of'], 'f16a') self.assertEqual(f16b['primary-set'], False) - self.assertEqual(f16b['author'], 'James T Kirk') + + authorsArray = f16b['authors'] + self.assertNotIn('author', f16b) + self.assertEqual(len(authorsArray), 2) + + author0 = authorsArray[0] + self.assertEqual(author0['name'], 'James T Kirk') + self.assertEqual(author0['nick'], 'starlover') f16c = next(v for v in variants if v['id'] == 'f16c') self.assertEqual(f16c['variant-of'], 'f16a') self.assertEqual(f16c['primary-set'], False) - self.assertEqual(f16c['author'], 'Wilbur Wright') + authorsArray = f16c['authors'] + self.assertNotIn('author', f16c) + self.assertEqual(len(authorsArray), 2) + # test some older constructs for compat + def test_scan_dir_legacy(self): + (pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/c172", []) + + self.assertEqual(pkg['id'], 'c172') + self.assertEqual(pkg['author'], 'Wilbur Wright') def test_extract_previews(self): info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"]) @@ -90,13 +119,25 @@ class UpdateCatalogTests(unittest.TestCase): self.assertEqual(parsedPkgNode.getValue('name'), pkg['name']); self.assertEqual(parsedPkgNode.getValue('description'), pkg['description']); - self.assertEqual(parsedPkgNode.getValue('author'), "Wilbur Wright"); self.assertEqual(parsedPkgNode.getValue('minimum-fg-version'), "2017.4"); parsedVariants = parsedPkgNode.getChildren("variant") self.assertEqual(len(parsedVariants), 3) +# author data verification + self.assertFalse(parsedPkgNode.hasChild('author')); + parsedAuthors = parsedPkgNode.getChild("authors").getChildren('author') + + self.assertEqual(len(parsedAuthors), 2) + author1 = parsedAuthors[0] + self.assertEqual(author1.getValue("name"), "Wilbur Wright") + self.assertEqual(author1.getValue("nick"), "wilburw") + self.assertEqual(author1.getValue("email"), "ww@wright.com") + + author2 = parsedAuthors[1] + self.assertEqual(author2.getValue("name"), "Orville Wright") + f16ANode = parsedPkgNode self.assertEqual(f16ANode.getValue('name'), 'F16-A'); @@ -107,11 +148,18 @@ class UpdateCatalogTests(unittest.TestCase): if (var['id'] == 'f16-trainer'): self.assertEqual(pv.getValue('variant-of'), '_primary_') - self.assertEqual(pv.getValue('author'), "Wilbur Wright"); + # self.assertEqual(pv.getValue('author'), "Wilbur Wright"); elif (var['id'] == 'f16b'): self.assertEqual(pv.getValue('variant-of'), 'f16a') self.assertEqual(pv.getValue('description'), 'The F16-B is an upgraded version of the F16A.') - self.assertEqual(pv.getValue('author'), "James T Kirk"); + + # variant author verification + parsedAuthors = pv.getChild("authors").getChildren('author') + author1 = parsedAuthors[0] + self.assertEqual(author1.getValue("name"), "James T Kirk") + self.assertEqual(author1.getValue("nick"), "starlover") + self.assertEqual(author1.getValue("email"), "shatner@enterprise.com") + self.assertEqual(author1.getValue("description"), "Everything") def test_minimalAircraft(self): # test an aircraft with a deliberately spartan -set.xml file with