synchronization inspirations between entries and inspirations list

This commit is contained in:
Trilarion
2020-09-06 10:05:36 +02:00
parent 469f9fa690
commit cd67ffe536
12 changed files with 384 additions and 176 deletions

View File

@ -2,92 +2,110 @@
Maintenance of inspirations.md and synchronization with the inspirations in the entries.
"""
import time
from utils import constants as c, utils, osg, osg_ui
from utils import osg_wikipedia
from utils import osg, osg_ui
valid_duplicates = ('Age of Empires', 'ARMA', 'Catacomb', 'Civilization', 'Company of Heroes', 'Descent', 'Duke Nukem', 'Dungeon Keeper',
'Final Fantasy', 'Heroes of Might and Magic', 'Jazz Jackrabbit', 'Marathon', 'Master of Orion', 'Quake',
'RollerCoaster Tycoon', 'Star Wars Jedi Knight', 'The Settlers', 'Ultima', 'Ship Simulator')
def check_for_duplicates():
"""
class InspirationMaintainer:
:param inspirations:
:return:
"""
print('\nduplicate check')
inspiration_names = [x['name'] for x in inspirations]
for index, name in enumerate(inspiration_names):
for other_name in inspiration_names[index+1:]:
if osg.name_similarity(name, other_name) > 0.8:
print(' {} - {} is similar'.format(name, other_name))
def __init__(self):
self.inspirations = None
self.entries = None
def test():
# assemble info
t0 = time.process_time()
entries = osg.read_entries()
print('took {}s'.format(time.process_time()-t0))
t0 = time.process_time()
# entries = osg.assemble_infos()
# osg.write_entries(entries)
print('took {}s'.format(time.process_time()-t0))
def read_inspirations(self):
self.inspirations = osg.read_inspirations()
print('{} inspirations read'.format(len(self.inspirations)))
def write_inspirations(self):
if not self.inspirations:
print('inspirations not yet loaded')
return
osg.write_inspirations(self.inspirations)
print('inspirations written')
# assemble inspirations info from entries
entries_inspirations = {}
for entry in entries:
entry_name = entry['name']
keywords = entry['keywords']
entry_inspirations = [x for x in keywords if x.startswith('inspired by')]
if entry_inspirations:
entry_inspirations = entry_inspirations[0][len('inspired by'):]
entry_inspirations = entry_inspirations.split('+')
entry_inspirations = [x.strip() for x in entry_inspirations]
for entry_inspiration in entry_inspirations:
if entry_inspiration in entries_inspirations:
entries_inspirations[entry_inspiration].append(entry_name)
def check_for_duplicates(self):
if not self.inspirations:
print('inspirations not yet loaded')
return
inspiration_names = list(self.inspirations.keys())
for index, name in enumerate(inspiration_names):
for other_name in inspiration_names[index + 1:]:
if any((name.startswith(x) and other_name.startswith(x) for x in valid_duplicates)):
continue
if osg.name_similarity(name, other_name) > 0.8:
print(' {} - {} is similar'.format(name, other_name))
print('duplicates checked')
def check_for_orphans(self):
if not self.inspirations:
print('inspirations not yet loaded')
return
for inspiration in self.inspirations.values():
if not inspiration['inspired entries']:
print(' {} has no inspired entries'.format(inspiration['name']))
print('orphanes checked')
def check_for_missing_inspirations_in_entries(self):
if not self.inspirations:
print('inspirations not yet loaded')
return
if not self.entries:
print('entries not yet loaded')
return
for inspiration in self.inspirations.values():
inspiration_name = inspiration['name']
for entry_name in inspiration['inspired entries']:
x = [x for x in self.entries if x['title'] == entry_name]
assert len(x) <= 1
if not x:
print('Entry "{}" listed in inspiration "{}" but this entry does not exist'.format(entry_name, inspiration_name))
else:
entries_inspirations[entry_inspiration] = [ entry_name ]
print('{} inspirations in the entries'.format(len(entries_inspirations)))
entry = x[0]
if 'inspirations' not in entry or inspiration_name not in entry['inspirations']:
print('Entry "{}" listed in inspiration "{}" but not listed in this entry'.format(entry_name, inspiration_name))
print('missed inspirations checked')
# first check if all inspiration in entries are also in inspirations
inspiration_names = [x['name'] for x in inspirations]
for inspiration, entries in entries_inspirations.items():
if inspiration not in inspiration_names:
print('new inspiration {} for games {}'.format(inspiration, ', '.join(entries)))
similar_names = [x for x in inspiration_names if osg.name_similarity(inspiration, x) > 0.8]
if similar_names:
print(' similar names {}'.format(', '.join(similar_names)))
def update_inspired_entries(self):
if not self.inspirations:
print('inspirations not yet loaded')
return
if not self.entries:
print('entries not yet loaded')
return
# loop over all inspirations and delete inspired entries
for inspiration in self.inspirations.values():
inspiration['inspired entries'] = []
# loop over all entries and add to inspirations of entry
for entry in self.entries:
entry_name = entry['title']
for inspiration in entry.get('inspirations', []):
if inspiration in self.inspirations:
self.inspirations[inspiration]['inspired entries'].append(entry_name)
else:
self.inspirations[inspiration] = {'name': inspiration, 'inspired entries': [entry_name]}
print('inspired entries updated')
# now the other way around
for index, name in enumerate(inspiration_names):
if name not in entries_inspirations:
print('potential removed inspiration {} from games {}'.format(name, inspirations[index]['inspired entries']))
similar_names = [x for x in entries_inspirations.keys() if osg.name_similarity(name, x) > 0.8]
if similar_names:
print(' similar names {}'.format(', '.join(similar_names)))
def read_entries(self):
self.entries = osg.read_entries()
print('{} entries read'.format(len(self.entries)))
def read_inspirations():
inspirations = osg.read_inspirations_info()
print('{} inspirations in the inspirations database'.format(len(inspirations)))
def write_inspirations():
osg.write_inspirations_info(inspirations)
print('inspirations written')
if __name__ == "__main__":
inspirations = osg.read_inspirations_info()
osg.write_inspirations_info(inspirations)
inspirations = None
entries = None
m = InspirationMaintainer()
actions = {
'Read inspirations': read_inspirations,
'Write inspirations': write_inspirations,
'Check for duplicates': check_for_duplicates,
'Read inspirations': m.read_inspirations,
'Write inspirations': m.write_inspirations,
'Check for duplicates': m.check_for_duplicates,
'Check for orphans': m.check_for_orphans,
'Check for inspirations not listed': m.check_for_missing_inspirations_in_entries,
'Update inspirations from entries': m.update_inspired_entries,
'Read entries': m.read_entries
}
osg_ui.run_simple_button_app('Maintenance inspirations', actions)

View File

@ -476,7 +476,7 @@ def write_developer_info(developers):
utils.write_text(developer_file, content)
def read_inspirations_info():
def read_inspirations():
"""
Reads the info list about the games originals/inspirations from inspirations.md using the Lark parser grammar
in grammar_listing.lark
@ -508,17 +508,23 @@ def read_inspirations_info():
duplicate_names = (name for name in names if names.count(name) > 1)
duplicate_names = set(duplicate_names) # to avoid duplicates in duplicate_names
if duplicate_names:
print('Warning: duplicate inspiration names: {}'.format(', '.join(duplicate_names)))
raise RuntimeError('Duplicate inspiration names: {}'.format(', '.join(duplicate_names)))
# convert to dictionary
inspirations = {x['name']: x for x in inspirations}
return inspirations
def write_inspirations_info(inspirations):
def write_inspirations(inspirations):
"""
Given an internal list of inspirations, write it into the inspirations file
Given an internal dictionary of inspirations, write it into the inspirations file
:param inspirations:
:return:
"""
# convert dictionary to list
inspirations = list(inspirations.values())
# comment
content = '{}\n'.format(generic_comment_string)
@ -544,7 +550,7 @@ def write_inspirations_info(inspirations):
field = field.capitalize()
# lists get special treatment
if isinstance(value, list):
value.sort(key=str.casefold)
value.sort(key=str.casefold) # sorted alphabetically
value = [x if not ',' in x else '"{}"'.format(x) for x in value] # surround those with a comma with quotation marks
value = ', '.join(value)
content += '- {}: {}\n'.format(field, value)

View File

@ -2,9 +2,17 @@
Simple UI helpers with PyQt
"""
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
def exception_hook(type, value, traceback):
"""
Use sys.__excepthook__, the standard hook.
"""
sys.__excepthook__(type, value, traceback)
def run_simple_button_app(title, actions):
"""
@ -12,6 +20,9 @@ def run_simple_button_app(title, actions):
:param actions:
:return:
"""
# fix PyQt5 eating exceptions (see http://stackoverflow.com/q/14493081/1536976)
sys.excepthook = exception_hook
# create app
app = QtWidgets.QApplication([])