improved static website

This commit is contained in:
Trilarion
2020-09-21 10:23:12 +02:00
parent c18b225780
commit 9d65ee912b
16 changed files with 381 additions and 155 deletions

View File

@ -4,15 +4,41 @@ Generates the static website
Uses Jinja2 (see https://jinja.palletsprojects.com/en/2.11.x/)
"""
# TODO index.html tiles, content
# TODO index.html image
# TODO index.html only count games
# TODO Font awesome 5 (icons for OS, for Github, Gitlab and maybe others)
# TODO contribute.html tiles? content
# TODO games pages links to licenses (wikipedia)
# TODO games pages edit/contribute button
# TODO indexes: make categories bold that have a certain amount of entries!
# TODO everywhere: style URLs (Github, Wikipedia, Internet archive, SourceForge, ...)
# TODO developers pages links to games and more information, styles, block around entry, edit/contribute button
# TODO inspirations pages, add link to games and more information, styles, block around entry, edit/contribute button
# TODO navbar add is active
# TODO statistics page: better and more statistics with links where possible
# TODO meaningful information (links, license, last updated with lower precision)
# TODO singular, plural everywhere (game, entries, items)
import os
import shutil
import math
import datetime
from functools import partial
from utils import osg, constants as c, utils
from jinja2 import Environment, FileSystemLoader
import html5lib
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
extra = '0'
extended_alphabet = alphabet + extra
games_path = 'games'
inspirations_path = 'inspirations'
developers_path = 'developers'
html5parser = html5lib.HTMLParser(strict=True)
alphabet = 'abcdefghijklmnopqrstuvwxyz'
extended_alphabet = '0' + alphabet
def write(text, file):
"""
@ -20,14 +46,23 @@ def write(text, file):
:param text:
:param file:
"""
# validate text
try:
html5parser.parse(text)
except Exception as e:
utils.write_text(os.path.join(c.web_path, 'invalid.html'), text) # for further checking with https://validator.w3.org/
raise RuntimeError(e)
# output file
file = os.path.join(c.web_path, file)
# create output directory if necessary
containing_dir = os.path.dirname(file)
if not os.path.isdir(containing_dir):
os.mkdir(containing_dir)
# write text
utils.write_text(file, text)
def sort_into_categories(list, categories, fit, unknown_category_name):
def sort_into_categories(list, categories, fit, unknown_category_name=None):
"""
:param list:
@ -46,16 +81,8 @@ def sort_into_categories(list, categories, fit, unknown_category_name):
categorized_sublists[unknown_category_name] = sublist
return categorized_sublists
def sort_by_alphabet(list, key):
"""
:param list:
:param key:
:return:
"""
return sort_into_categories(list, alphabet, lambda item, category: item[key].lower().startswith(category), '0')
def divide_in_columns(categorized_lists, key):
def divide_in_columns(categorized_lists, transform):
"""
:param categorized_lists:
@ -66,7 +93,8 @@ def divide_in_columns(categorized_lists, key):
entries = {}
for category in categorized_lists.keys():
e = categorized_lists[category]
e = [e[key] for e in e]
# transform entry
e = [transform(e) for e in e]
# divide in three equal lists
n = len(e)
n1 = math.ceil(n/3)
@ -76,6 +104,118 @@ def divide_in_columns(categorized_lists, key):
return {'number_entries': number_entries, 'entries': entries}
def url_to(current, target):
"""
:param current:
:param target:
:return:
"""
# split by slash
if current:
current = current.split('/')
target = target.split('/')
# reduce by common elements
while len(current) > 0 and len(target) > 1 and current[0] == target[0]:
current = current[1:]
target = target[1:]
# add .. as often as length of current still left
target = ['..'] * len(current) + target
# join by slash again
url = '/'.join(target)
return url
def preprocess(list, key, path):
"""
:param list:
:param key:
:return:
"""
_ = set()
for item in list:
# add unique anchor ref
anchor = osg.canonical_name(item[key])
while anchor in _:
anchor += 'x'
_.add(anchor)
item['anchor-id'] = anchor
# for alphabetic sorting
start = item[key][0].upper()
if not start in alphabet:
start = extra
item['letter'] = start
item['href'] = os.path.join(path, '{}.html#{}'.format(start, anchor))
def game_index(entry):
e = {
'name': entry['Title'],
'href': entry['href'],
'anchor-id': entry['anchor-id']
}
tags = []
if 'beta' in entry['State']:
tags.append('beta')
if osg.is_inactive(entry):
tags.append('inactive since {}'.format(osg.extract_inactive_year(entry)))
if tags:
e['tags'] = ', '.join(tags)
return e
def inspiration_index(inspiration):
e = {
'name': inspiration['Name'],
'href': inspiration['href'],
'anchor-id': inspiration['anchor-id'],
}
n = len(inspiration['Inspired entries'])
if n > 1:
e['tags'] = n
return e
def developer_index(developer):
e = {
'name': developer['Name'],
'href': developer['href'],
'anchor-id': developer['anchor-id']
}
n = len(developer['Games'])
if n > 1:
e['tags'] = n
return e
def add_entries_links_to_inspirations(inspirations, entries):
entries_references = {entry['Title']:entry['href'] for entry in entries}
for inspiration in inspirations:
inspired_entries = inspiration['Inspired entries']
inspired_entries = [(entries_references[entry], entry) for entry in inspired_entries]
inspiration['Inspired entries'] = inspired_entries
def add_entries_links_to_developers(developers, entries):
entries_references = {entry['Title']:entry['href'] for entry in entries}
for developer in developers:
developed_entries = developer['Games']
developed_entries = [(entries_references[entry], entry) for entry in developed_entries]
developer['Games'] = developed_entries
def add_inspirations_and_developers_links_to_entries(entries, inspirations, developers):
inspirations_references = {inspiration['Name']: inspiration['href'] for inspiration in inspirations}
developer_references = {developer['Name']: developer['href'] for developer in developers}
for entry in entries:
if 'Inspirations' in entry:
entry['Inspirations'] = [(inspirations_references[inspiration.value], inspiration.value) for inspiration in entry['Inspirations']]
if 'Developer' in entry:
entry['Developer'] = [(developer_references[developer.value], developer.value) for developer in entry['Developer']]
def generate(entries, inspirations, developers):
"""
@ -84,21 +224,29 @@ def generate(entries, inspirations, developers):
:param developers:
"""
# add anchor ref () to every entry
for entry in entries:
entry['title-anchor'] = osg.canonical_entry_name(entry['Title'])
# preprocess
preprocess(entries, 'Title', games_path)
preprocess(inspirations, 'Name', inspirations_path)
preprocess(developers, 'Name', developers_path)
# set internal links up
add_entries_links_to_inspirations(inspirations, entries)
add_entries_links_to_developers(developers, entries)
add_inspirations_and_developers_links_to_entries(entries, inspirations, developers)
# sort into categories
games_by_alphabet = sort_into_categories(entries, extended_alphabet, lambda item, category: category == item['letter'])
inspirations_by_alphabet = sort_into_categories(inspirations, extended_alphabet, lambda item, category: category == item['letter'])
developers_by_alphabet = sort_into_categories(developers, extended_alphabet, lambda item, category: category == item['letter'])
genres = [keyword.capitalize() for keyword in c.recommended_keywords]
games_by_genre = sort_into_categories(entries, genres, lambda item, category: category.lower() in item['Keywords'])
games_by_platform = sort_into_categories(entries, c.valid_platforms, lambda item, category: category in item.get('Platform', []), 'Unspecified')
games_by_language = sort_into_categories(entries, c.known_languages, lambda item, category: category in item['Code language'])
# base dictionary
base = {
'title': 'OSGL',
'paths': {
'css': 'css/bulma.min.css',
'index': 'index.html',
'index-games': 'games/index.html',
'index-developers': 'developers/index.html',
'index-inspirations': 'inspirations/index.html',
'index-statistics': 'index-statistics'
},
'creation-date': datetime.datetime.utcnow()
}
@ -114,80 +262,26 @@ def generate(entries, inspirations, developers):
template_categorical_index = environment.get_template('categorical_index.jinja')
template_list = environment.get_template('list.jinja')
# top level folder
base['url_to'] = partial(url_to, '')
# index.html
index = {'number_games': len(entries)} # TODO only count games
index = {'number_games': len(entries)}
template = environment.get_template('index.jinja')
write(template.render(index=index), 'index.html')
# generate games pages
games_by_alphabet = sort_by_alphabet(entries, 'Title')
template = environment.get_template('games_for_letter.jinja')
for letter in extended_alphabet:
write(template.render(letter=letter, games=games_by_alphabet[letter]), os.path.join('games', '{}.html'.format(letter.capitalize())))
# contribute.html
template = environment.get_template('contribute.jinja')
write(template.render(), 'contribute.html')
# generate games index
index = divide_in_columns(games_by_alphabet, 'Title')
index['title'] = 'Games index'
index['categories'] = extended_alphabet
write(template_categorical_index.render(index=index), os.path.join('games', 'index.html'))
# statistics
## inspirations
inspirations_by_alphabet = sort_by_alphabet(inspirations, 'Name')
# inspirations index
index = divide_in_columns(inspirations_by_alphabet, 'Name')
index['title'] = 'Inspirations index'
index['categories'] = extended_alphabet
write(template_categorical_index.render(index=index), os.path.join('inspirations', 'index.html'))
# inspirations single pages
template = environment.get_template('inspirations_for_letter.jinja')
for letter in extended_alphabet:
write(template.render(letter=letter, inspirations=inspirations_by_alphabet[letter]), os.path.join('inspirations', '{}.html'.format(letter.capitalize())))
## developers
# developers single pages
developers_by_alphabet = sort_by_alphabet(developers, 'Name')
template = environment.get_template('developers_for_letter.jinja')
for letter in extended_alphabet:
write(template.render(letter=letter, developers=developers_by_alphabet[letter]), os.path.join('developers', '{}.html'.format(letter.capitalize())))
# developers index
index = divide_in_columns(developers_by_alphabet, 'Name')
index['title'] = 'Developers index'
index['categories'] = extended_alphabet
write(template_categorical_index.render(index=index), os.path.join('developers', 'index.html'))
## genres
genres = c.recommended_keywords
games_by_genre = sort_into_categories(entries, genres, lambda item, category: category in item['Keywords'], None)
index = divide_in_columns(games_by_genre, 'Title')
index['title'] = 'Games by genre'
index['categories'] = genres
write(template_categorical_index.render(index=index), os.path.join('games', 'genres.html'))
## games by language TODO make categories bold that have a certain amount of entries!
languages = c.known_languages
games_by_language = sort_into_categories(entries, languages, lambda item, category: category in item['Code language'], None)
index = divide_in_columns(games_by_language, 'Title')
index['title'] = 'Games by language'
index['categories'] = languages # it's fine if they get capitalized, because they are already capitalized
write(template_categorical_index.render(index=index), os.path.join('games', 'languages.html'))
## games by platform
platforms = c.valid_platforms
games_by_platform = sort_into_categories(entries, platforms, lambda item, category: category in item.get('Platform', []), 'Unspecified')
index = divide_in_columns(games_by_platform, 'Title')
index['title'] = 'Games by platform'
index['categories'] = platforms # TODO (do not capitalize automatically)
write(template_categorical_index.render(index=index), os.path.join('games', 'platforms.html'))
## statistics
# index
template = environment.get_template('statistics_index.jinja')
write(template.render(), os.path.join('statistics', 'index.html'))
# preparation
template = environment.get_template('statistics.jinja')
data = {
'title': 'Statistics',
'sections': []
}
# build-systems
build_systems = []
@ -201,12 +295,78 @@ def generate(entries, inspirations, developers):
unique_build_systems = [(l, build_systems.count(l)) for l in unique_build_systems]
unique_build_systems.sort(key=lambda x: str.casefold(x[0])) # first sort by name
unique_build_systems.sort(key=lambda x: -x[1]) # then sort by occurrence (highest occurrence first)
data = {
section = {
'title': 'Build system',
'items': ['{} ({})'.format(*item) for item in unique_build_systems]
}
write(template_list.render(data=data), os.path.join('statistics', 'build-systems.html'))
data['sections'].append(section)
write(template.render(data=data), os.path.join('statistics.html'))
# games folder
base['url_to'] = partial(url_to, games_path)
# generate games pages
template = environment.get_template('games_for_letter.jinja')
for letter in extended_alphabet:
data = {
'title': 'Games with {}'.format(letter.capitalize()),
'games': games_by_alphabet[letter]
}
write(template.render(data=data), os.path.join(games_path, '{}.html'.format(letter.capitalize())))
# generate games index
index = divide_in_columns(games_by_alphabet, game_index)
index['title'] = 'Alphabetical index'
index['categories'] = extended_alphabet
write(template_categorical_index.render(index=index), os.path.join(games_path, 'index.html'))
# genres
index = divide_in_columns(games_by_genre, game_index)
index['title'] = 'Games by genre'
index['categories'] = genres
write(template_categorical_index.render(index=index), os.path.join(games_path, 'genres.html'))
# games by language
index = divide_in_columns(games_by_language, game_index)
index['title'] = 'Games by language'
index['categories'] = c.known_languages
write(template_categorical_index.render(index=index), os.path.join(games_path, 'languages.html'))
# games by platform
index = divide_in_columns(games_by_platform, game_index)
index['title'] = 'Games by platform'
index['categories'] = c.valid_platforms + ('Unspecified',)
write(template_categorical_index.render(index=index), os.path.join(games_path, 'platforms.html'))
# inspirations folder
base['url_to'] = partial(url_to, inspirations_path)
# inspirations
# inspirations index
index = divide_in_columns(inspirations_by_alphabet, inspiration_index)
index['title'] = 'Inspirations index'
index['categories'] = extended_alphabet
write(template_categorical_index.render(index=index), os.path.join(inspirations_path, 'index.html'))
# inspirations single pages
template = environment.get_template('inspirations_for_letter.jinja')
for letter in extended_alphabet:
write(template.render(letter=letter, inspirations=inspirations_by_alphabet[letter]), os.path.join(inspirations_path, '{}.html'.format(letter.capitalize())))
# developers folder
base['url_to'] = partial(url_to, developers_path)
# developers single pages
template = environment.get_template('developers_for_letter.jinja')
for letter in extended_alphabet:
write(template.render(letter=letter, developers=developers_by_alphabet[letter]), os.path.join(developers_path, '{}.html'.format(letter.capitalize())))
# developers index
index = divide_in_columns(developers_by_alphabet, developer_index)
index['title'] = 'Developers index'
index['categories'] = extended_alphabet
write(template_categorical_index.render(index=index), os.path.join(developers_path, 'index.html'))
if __name__ == "__main__":