maintenance of inspirations
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
start: entry*
|
||||
entry: "##" name "(" _NUMBER ")\n" property+
|
||||
entry: "##" name "[" _NUMBER "]\n" property+
|
||||
property: "-" _key ":" _values "\n"
|
||||
_key: /(?! ).+?(?=:)(?<! )/ // key: everything until next ":", not beginning or ending with a space
|
||||
_values: [_value ("," _value)*]
|
||||
@ -7,7 +7,7 @@ _value: quoted_value | unquoted_value
|
||||
quoted_value: /\".+?\"/ // with quotation marks, can contain commas
|
||||
unquoted_value: /(?![ \"])[^,\n]+(?<![ \"])/ // cannot contain commas, cannot start or end with quotation mark
|
||||
|
||||
name: /(?! ).+?(?= \()/ // developer name: everything until " ("
|
||||
name: /(?! ).+?(?= \[)/ // developer name: everything until " ["
|
||||
|
||||
_NUMBER: /[0-9]+/
|
||||
|
||||
|
@ -4,6 +4,7 @@ stored Git repositories.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from utils import constants as c, utils, osg, osg_github
|
||||
@ -24,7 +25,11 @@ if __name__ == "__main__":
|
||||
|
||||
# read developer info
|
||||
developer_info = osg.read_developer_info()
|
||||
osg.write_developer_info(developer_info) # write again just to make nice
|
||||
osg.write_developer_info(developer_info) # write again just to make it nice and as sanity check
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
# assemble info
|
||||
entries = osg.assemble_infos()
|
||||
|
@ -1,9 +1,70 @@
|
||||
from utils import constants as c, utils, osg
|
||||
"""
|
||||
Maintenance of inspirations.md and synchronization with the inspirations in the entries.
|
||||
"""
|
||||
|
||||
from utils import constants as c, utils, osg, osg_ui
|
||||
|
||||
|
||||
def duplicate_check():
|
||||
"""
|
||||
|
||||
: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) > similarity_threshold:
|
||||
print(' {} - {} is similar'.format(name, other_name))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
similarity_threshold = 0.8
|
||||
|
||||
# load inspirations
|
||||
inspirations = osg.read_inspirations_info()
|
||||
print('{} inspirations in the inspirations database'.format(len(inspirations)))
|
||||
osg.write_inspirations_info(inspirations) # write again just to check integrity
|
||||
|
||||
osg_ui.run_simple_button_app('Maintenance inspirations', (('Duplicate check', duplicate_check),))
|
||||
|
||||
|
||||
|
||||
# assemble info
|
||||
# entries = osg.assemble_infos()
|
||||
entries = osg.assemble_infos()
|
||||
|
||||
# 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)
|
||||
else:
|
||||
entries_inspirations[entry_inspiration] = [ entry_name ]
|
||||
print('{} inspirations in the entries'.format(len(entries_inspirations)))
|
||||
|
||||
# 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)))
|
||||
|
||||
# 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)))
|
@ -92,4 +92,4 @@ general_code_dependencies_without_entry = {'OpenGL': 'https://www.opengl.org/',
|
||||
valid_developer_fields = ('name', 'games', 'contact', 'organization', 'home')
|
||||
|
||||
# inspiration/original game information (in the file all fields will be capitalized)
|
||||
valid_inspiration_fields = ('name', 'inspired entries')
|
||||
valid_inspiration_fields = ('name', 'inspired entries', 'media')
|
@ -399,14 +399,13 @@ def read_developer_info():
|
||||
if field not in valid_developer_fields:
|
||||
raise RuntimeError('Unknown developer field "{}" for developer: {}.'.format(field, dev['name']))
|
||||
# strip from name or organization (just in case)
|
||||
for field in ('name', 'organization'):
|
||||
for field in ('name', ):
|
||||
if field in dev:
|
||||
dev[field] = dev[field].strip()
|
||||
# split games, contact (are lists)
|
||||
for field in ('games', 'contact'):
|
||||
if field in dev:
|
||||
content = dev[field]
|
||||
content = content.split(',')
|
||||
content = [x.strip() for x in content]
|
||||
dev[field] = content
|
||||
# check for duplicate names entries
|
||||
@ -427,28 +426,31 @@ def write_developer_info(developers):
|
||||
content = '{}\n'.format(generic_comment_string)
|
||||
|
||||
# number of developer
|
||||
content += '# Developer ({})\n\n'.format(len(developers))
|
||||
content += '# Developer [{}]\n\n'.format(len(developers))
|
||||
|
||||
# sort by name
|
||||
developers.sort(key=lambda x: str.casefold(x['name']))
|
||||
|
||||
# iterate over them
|
||||
for dev in developers:
|
||||
keys = list(dev.keys())
|
||||
# developer name
|
||||
content += '## {} ({})\n\n'.format(dev['name'], len(dev['games']))
|
||||
content += '## {} [{}]\n\n'.format(dev['name'], len(dev['games']))
|
||||
keys.remove('name')
|
||||
|
||||
# games
|
||||
content += '- Games: {}\n'.format(', '.join(sorted(dev['games'], key=str.casefold)))
|
||||
|
||||
# all the remaining in alphabetical order
|
||||
for field in sorted(dev.keys()):
|
||||
if field not in ('name', 'games'):
|
||||
value = dev[field]
|
||||
field = field.capitalize()
|
||||
if isinstance(value, str):
|
||||
content += '- {}: {}\n'.format(field, value)
|
||||
else:
|
||||
content += '- {}: {}\n'.format(field, ', '.join(sorted(value, key=str.casefold)))
|
||||
# all the remaining in alphabetical order, but 'games' first
|
||||
keys.remove('games')
|
||||
keys.sort()
|
||||
keys = ['games'] + keys
|
||||
for field in keys:
|
||||
value = dev[field]
|
||||
field = field.capitalize()
|
||||
# lists get special treatment
|
||||
if isinstance(value, list):
|
||||
value.sort(key=str.casefold)
|
||||
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)
|
||||
content += '\n'
|
||||
|
||||
# write
|
||||
@ -502,29 +504,31 @@ def write_inspirations_info(inspirations):
|
||||
content = '{}\n'.format(generic_comment_string)
|
||||
|
||||
# number of developer
|
||||
content += '# Inspirations ({})\n\n'.format(len(inspirations))
|
||||
content += '# Inspirations [{}]\n\n'.format(len(inspirations))
|
||||
|
||||
# sort by name
|
||||
inspirations.sort(key=lambda x: str.casefold(x['name']))
|
||||
|
||||
# iterate over them
|
||||
for inspiration in inspirations:
|
||||
keys = list(inspiration.keys())
|
||||
# inspiration name
|
||||
content += '## {} ({})\n\n'.format(inspiration['name'], len(inspiration['inspired entries']))
|
||||
content += '## {} [{}]\n\n'.format(inspiration['name'], len(inspiration['inspired entries']))
|
||||
keys.remove('name')
|
||||
|
||||
# games
|
||||
content += '- Inspired entries: {}\n'.format(
|
||||
', '.join(sorted(inspiration['inspired entries'], key=str.casefold)))
|
||||
|
||||
# all the remaining in alphabetical order
|
||||
for field in sorted(inspiration.keys()):
|
||||
if field not in ('name', 'inspired entries'):
|
||||
value = inspiration[field]
|
||||
field = field.capitalize()
|
||||
if isinstance(value, str):
|
||||
content += '- {}: {}\n'.format(field, value)
|
||||
else:
|
||||
content += '- {}: {}\n'.format(field, ', '.join(sorted(value, key=str.casefold)))
|
||||
# all the remaining in alphabetical order, but "inspired entries" first
|
||||
keys.remove('inspired entries')
|
||||
keys.sort()
|
||||
keys = ['inspired entries'] + keys
|
||||
for field in keys:
|
||||
value = inspiration[field]
|
||||
field = field.capitalize()
|
||||
# lists get special treatment
|
||||
if isinstance(value, list):
|
||||
value.sort(key=str.casefold)
|
||||
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)
|
||||
content += '\n'
|
||||
|
||||
# write
|
||||
|
32
code/utils/osg_ui.py
Normal file
32
code/utils/osg_ui.py
Normal file
@ -0,0 +1,32 @@
|
||||
"""
|
||||
Simple UI helpers with PyQt
|
||||
"""
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
def run_simple_button_app(title, actions):
|
||||
"""
|
||||
|
||||
:param title:
|
||||
:param actions:
|
||||
:return:
|
||||
"""
|
||||
# create app
|
||||
app = QtWidgets.QApplication([])
|
||||
|
||||
# create single widget
|
||||
widget = QtWidgets.QWidget()
|
||||
widget.setWindowTitle(title)
|
||||
widget.setMinimumSize(200, 400)
|
||||
|
||||
# add actions
|
||||
layout = QtWidgets.QVBoxLayout(widget)
|
||||
for name, action in actions:
|
||||
button = QtWidgets.QPushButton(name)
|
||||
button.clicked.connect(action)
|
||||
layout.addWidget(button)
|
||||
|
||||
# execute app
|
||||
widget.show()
|
||||
return app.exec_()
|
Reference in New Issue
Block a user