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
This commit is contained in:
+53
-6
@@ -10,6 +10,16 @@ import sys
|
|||||||
import catalogTags
|
import catalogTags
|
||||||
|
|
||||||
CATALOG_VERSION = 4
|
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
|
# xml node (robust) get text helper
|
||||||
def get_xml_text(e):
|
def get_xml_text(e):
|
||||||
@@ -43,13 +53,17 @@ def scan_set_file(aircraft_dir, set_file, includes):
|
|||||||
variant = {}
|
variant = {}
|
||||||
name = sim_node.getValue("description", None)
|
name = sim_node.getValue("description", None)
|
||||||
if (name == None or len(name) == 0):
|
if (name == None or len(name) == 0):
|
||||||
print "Set file " + set_file + " is missing a <description>, skipping"
|
warning("Set file " + set_file + " is missing a <description>, skipping")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
variant['name'] = name
|
variant['name'] = name
|
||||||
variant['status'] = sim_node.getValue("status", None)
|
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)
|
variant['author'] = sim_node.getValue("author", None)
|
||||||
|
|
||||||
if sim_node.hasChild('long-description'):
|
if sim_node.hasChild('long-description'):
|
||||||
@@ -94,7 +108,7 @@ def extract_previews(previews_node, aircraft_dir):
|
|||||||
# check path exists in base-name-dir
|
# check path exists in base-name-dir
|
||||||
fullPath = os.path.join(aircraft_dir, previewPath)
|
fullPath = os.path.join(aircraft_dir, previewPath)
|
||||||
if not os.path.isfile(fullPath):
|
if not os.path.isfile(fullPath):
|
||||||
print "Bad preview path, skipping:" + fullPath
|
warning("Bad preview path, skipping:" + fullPath)
|
||||||
continue
|
continue
|
||||||
result.append({'type':previewType, 'path':previewPath})
|
result.append({'type':previewType, 'path':previewPath})
|
||||||
|
|
||||||
@@ -106,11 +120,25 @@ def extract_tags(tags_node, set_path):
|
|||||||
tag = node.value
|
tag = node.value
|
||||||
# check tag is in the allowed list
|
# check tag is in the allowed list
|
||||||
if not catalogTags.isValidTag(tag):
|
if not catalogTags.isValidTag(tag):
|
||||||
print "Unknown tag value:", tag, " in ", set_path
|
warning("Unknown tag value:" + tag + " in " + set_path)
|
||||||
result.append(tag)
|
result.append(tag)
|
||||||
|
|
||||||
return result
|
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
|
# scan all the -set.xml files in an aircraft directory. Returns a
|
||||||
# package dict and a list of variants.
|
# package dict and a list of variants.
|
||||||
def scan_aircraft_dir(aircraft_dir, includes):
|
def scan_aircraft_dir(aircraft_dir, includes):
|
||||||
@@ -187,6 +215,25 @@ def append_tag_nodes(node, variant):
|
|||||||
for tag in variant['tags']:
|
for tag in variant['tags']:
|
||||||
node.append(make_xml_leaf('tag', tag))
|
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):
|
def make_aircraft_node(aircraftDirName, package, variants, downloadBase):
|
||||||
#print "package:", package
|
#print "package:", package
|
||||||
#print "variants:", variants
|
#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('name', package['name']) )
|
||||||
package_node.append( make_xml_leaf('status', package['status']) )
|
package_node.append( make_xml_leaf('status', package['status']) )
|
||||||
|
|
||||||
if 'author' in package:
|
append_author_nodes(package_node, package)
|
||||||
package_node.append( make_xml_leaf('author', package['author']) )
|
|
||||||
|
|
||||||
if 'description' in package:
|
if 'description' in package:
|
||||||
package_node.append( make_xml_leaf('description', package['description']) )
|
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_preview_nodes(variant_node, variant, downloadBase, aircraftDirName)
|
||||||
append_tag_nodes(variant_node, variant)
|
append_tag_nodes(variant_node, variant)
|
||||||
|
append_author_nodes(variant_node, variant)
|
||||||
|
|
||||||
package_node.append( make_xml_leaf('dir', aircraftDirName) )
|
package_node.append( make_xml_leaf('dir', aircraftDirName) )
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<PropertyList>
|
||||||
|
<sim>
|
||||||
|
<name>c172</name>
|
||||||
|
<description>Cessna 172P</description>
|
||||||
|
<author>Wilbur Wright</author>
|
||||||
|
</sim>
|
||||||
|
|
||||||
|
</PropertyList>
|
||||||
@@ -1,7 +1,18 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<PropertyList>
|
<PropertyList>
|
||||||
<sim include="settings-common.xml">
|
<sim include="settings-common.xml">
|
||||||
<author>Wilbur Wright</author>
|
<authors>
|
||||||
|
<author n="0">
|
||||||
|
<name>Wilbur Wright</name>
|
||||||
|
<email>ww@wright.com</email>
|
||||||
|
<nick>wilburw</nick>
|
||||||
|
<description>Model, FDM and cockpit</description>
|
||||||
|
</author>
|
||||||
|
<author n="1">
|
||||||
|
<name>Orville Wright</name>
|
||||||
|
<description>Testing and systems</description>
|
||||||
|
</author>
|
||||||
|
</authors>
|
||||||
<tags>
|
<tags>
|
||||||
<tag>fighter</tag>
|
<tag>fighter</tag>
|
||||||
<tag>1980s</tag>
|
<tag>1980s</tag>
|
||||||
|
|||||||
@@ -6,7 +6,14 @@
|
|||||||
<long-description>The F16-B is an upgraded version of the F16A.</long-description>
|
<long-description>The F16-B is an upgraded version of the F16A.</long-description>
|
||||||
<variant-of>f16a</variant-of>
|
<variant-of>f16a</variant-of>
|
||||||
|
|
||||||
<author>James T Kirk</author>
|
<authors n="0">
|
||||||
|
<author n="0">
|
||||||
|
<name>James T Kirk</name>
|
||||||
|
<email>shatner@enterprise.com</email>
|
||||||
|
<nick>starlover</nick>
|
||||||
|
<description>Everything</description>
|
||||||
|
</author>
|
||||||
|
</authors>
|
||||||
</sim>
|
</sim>
|
||||||
|
|
||||||
</PropertyList>
|
</PropertyList>
|
||||||
|
|||||||
+55
-7
@@ -6,6 +6,8 @@ import os
|
|||||||
import catalog
|
import catalog
|
||||||
import lxml.etree as ET
|
import lxml.etree as ET
|
||||||
|
|
||||||
|
catalog.quiet = True
|
||||||
|
|
||||||
class UpdateCatalogTests(unittest.TestCase):
|
class UpdateCatalogTests(unittest.TestCase):
|
||||||
def test_scan_set(self):
|
def test_scan_set(self):
|
||||||
info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"])
|
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['name'], 'F16-A')
|
||||||
self.assertEqual(info['primary-set'], True)
|
self.assertEqual(info['primary-set'], True)
|
||||||
self.assertEqual(info['variant-of'], None)
|
self.assertEqual(info['variant-of'], None)
|
||||||
self.assertEqual(info['author'], 'Wilbur Wright')
|
|
||||||
self.assertEqual(info['rating_FDM'], 3)
|
self.assertEqual(info['rating_FDM'], 3)
|
||||||
self.assertEqual(info['rating_model'], 5)
|
self.assertEqual(info['rating_model'], 5)
|
||||||
self.assertEqual(len(info['tags']), 3)
|
self.assertEqual(len(info['tags']), 3)
|
||||||
self.assertEqual(info['minimum-fg-version'], '2017.4')
|
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):
|
def test_scan_dir(self):
|
||||||
(pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/f16", ["testData/OtherDir"])
|
(pkg, variants) = catalog.scan_aircraft_dir("testData/Aircraft/f16", ["testData/OtherDir"])
|
||||||
|
|
||||||
self.assertEqual(pkg['id'], 'f16a')
|
self.assertEqual(pkg['id'], 'f16a')
|
||||||
f16trainer = next(v for v in variants if v['id'] == 'f16-trainer')
|
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(len(variants), 3)
|
||||||
self.assertEqual(pkg['minimum-fg-version'], '2017.4')
|
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')
|
f16b = next(v for v in variants if v['id'] == 'f16b')
|
||||||
self.assertEqual(f16b['variant-of'], 'f16a')
|
self.assertEqual(f16b['variant-of'], 'f16a')
|
||||||
self.assertEqual(f16b['primary-set'], False)
|
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')
|
f16c = next(v for v in variants if v['id'] == 'f16c')
|
||||||
self.assertEqual(f16c['variant-of'], 'f16a')
|
self.assertEqual(f16c['variant-of'], 'f16a')
|
||||||
self.assertEqual(f16c['primary-set'], False)
|
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):
|
def test_extract_previews(self):
|
||||||
info = catalog.scan_set_file("testData/Aircraft/f16", "f16a-set.xml", ["testData/OtherDir"])
|
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('name'), pkg['name']);
|
||||||
self.assertEqual(parsedPkgNode.getValue('description'), pkg['description']);
|
self.assertEqual(parsedPkgNode.getValue('description'), pkg['description']);
|
||||||
self.assertEqual(parsedPkgNode.getValue('author'), "Wilbur Wright");
|
|
||||||
|
|
||||||
self.assertEqual(parsedPkgNode.getValue('minimum-fg-version'), "2017.4");
|
self.assertEqual(parsedPkgNode.getValue('minimum-fg-version'), "2017.4");
|
||||||
|
|
||||||
parsedVariants = parsedPkgNode.getChildren("variant")
|
parsedVariants = parsedPkgNode.getChildren("variant")
|
||||||
self.assertEqual(len(parsedVariants), 3)
|
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
|
f16ANode = parsedPkgNode
|
||||||
self.assertEqual(f16ANode.getValue('name'), 'F16-A');
|
self.assertEqual(f16ANode.getValue('name'), 'F16-A');
|
||||||
|
|
||||||
@@ -107,11 +148,18 @@ class UpdateCatalogTests(unittest.TestCase):
|
|||||||
|
|
||||||
if (var['id'] == 'f16-trainer'):
|
if (var['id'] == 'f16-trainer'):
|
||||||
self.assertEqual(pv.getValue('variant-of'), '_primary_')
|
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'):
|
elif (var['id'] == 'f16b'):
|
||||||
self.assertEqual(pv.getValue('variant-of'), 'f16a')
|
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('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):
|
def test_minimalAircraft(self):
|
||||||
# test an aircraft with a deliberately spartan -set.xml file with
|
# test an aircraft with a deliberately spartan -set.xml file with
|
||||||
|
|||||||
Reference in New Issue
Block a user