dynamic html table with javascript, more screenshots

This commit is contained in:
Trilarion
2021-10-15 18:06:09 +02:00
parent 8486b618e1
commit df80f70125
125 changed files with 14005 additions and 238 deletions

View File

@ -7,10 +7,13 @@
<meta name="author" content="Trilarion">
<meta name="description" content="Infos and technical information about many open source games and frameworks.">
<title>{{ base['title'] }}</title>
<link rel="stylesheet" href="{{ base['url_to'](['css', 'bulma.min.css']) }}">
<link rel="stylesheet" href="{{ base['url_to'](['css', 'osgl.min.css']) }}">
{%- for css in base['css'] %}
<link rel="stylesheet" href="{{ base['url_to'](['css', css]) }}">
{%- endfor %}
<link rel="icon" type="image/svg+xml" href="{{ base['url_to'](['favicon.svg']) }}">
<script type="text/javascript" src="{{ base['url_to'](['js', 'osgl.js']) }}"></script>
{%- for js in base['js'] %}
<script type="text/javascript" src="{{ base['url_to'](['js', js]) }}"></script>
{%- endfor %}
</head>
<body>
{#- navigation bar -#}

View File

@ -0,0 +1,173 @@
.dataTable-wrapper.no-header .dataTable-container {
border-top: 1px solid #d9d9d9;
}
.dataTable-wrapper.no-footer .dataTable-container {
border-bottom: 1px solid #d9d9d9;
}
.dataTable-top,
.dataTable-bottom {
padding: 8px 10px;
}
.dataTable-top > nav:first-child,
.dataTable-top > div:first-child,
.dataTable-bottom > nav:first-child,
.dataTable-bottom > div:first-child {
float: left;
}
.dataTable-top > nav:last-child,
.dataTable-top > div:last-child,
.dataTable-bottom > nav:last-child,
.dataTable-bottom > div:last-child {
float: right;
}
.dataTable-selector {
padding: 6px;
}
.dataTable-input {
padding: 6px 12px;
}
.dataTable-info {
margin: 7px 0;
}
/* PAGER */
.dataTable-pagination ul {
margin: 0;
padding-left: 0;
}
.dataTable-pagination li {
list-style: none;
float: left;
}
.dataTable-pagination a {
border: 1px solid transparent;
float: left;
margin-left: 2px;
padding: 6px 12px;
position: relative;
text-decoration: none;
color: #333;
}
.dataTable-pagination a:hover {
background-color: #d9d9d9;
}
.dataTable-pagination .active a,
.dataTable-pagination .active a:focus,
.dataTable-pagination .active a:hover {
background-color: #d9d9d9;
cursor: default;
}
.dataTable-pagination .ellipsis a,
.dataTable-pagination .disabled a,
.dataTable-pagination .disabled a:focus,
.dataTable-pagination .disabled a:hover {
cursor: not-allowed;
}
.dataTable-pagination .disabled a,
.dataTable-pagination .disabled a:focus,
.dataTable-pagination .disabled a:hover {
cursor: not-allowed;
opacity: 0.4;
}
.dataTable-pagination .pager a {
font-weight: bold;
}
/* TABLE */
.dataTable-table {
max-width: 100%;
width: 100%;
border-spacing: 0;
border-collapse: separate;
}
.dataTable-table > tbody > tr > td,
.dataTable-table > tbody > tr > th,
.dataTable-table > tfoot > tr > td,
.dataTable-table > tfoot > tr > th,
.dataTable-table > thead > tr > td,
.dataTable-table > thead > tr > th {
vertical-align: top;
padding: 8px 10px;
}
.dataTable-table > thead > tr > th {
vertical-align: bottom;
text-align: left;
border-bottom: 1px solid #d9d9d9;
}
.dataTable-table > tfoot > tr > th {
vertical-align: bottom;
text-align: left;
border-top: 1px solid #d9d9d9;
}
.dataTable-table th {
vertical-align: bottom;
text-align: left;
}
.dataTable-table th a {
text-decoration: none;
color: inherit;
}
.dataTable-sorter {
display: inline-block;
height: 100%;
position: relative;
width: 100%;
}
.dataTable-sorter::before,
.dataTable-sorter::after {
content: "";
height: 0;
width: 0;
position: absolute;
right: 4px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
opacity: 0.2;
}
.dataTable-sorter::before {
border-top: 4px solid #000;
bottom: 0px;
}
.dataTable-sorter::after {
border-bottom: 4px solid #000;
border-top: 4px solid transparent;
top: 0px;
}
.asc .dataTable-sorter::after,
.desc .dataTable-sorter::before {
opacity: 0.6;
}
.dataTables-empty {
text-align: center;
}
.dataTable-top::after, .dataTable-bottom::after {
clear: both;
content: " ";
display: table;
}

View File

@ -7,6 +7,14 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
"""
# TODO simple data table with columns: name (homepage as link), keywords (at least essential ones), inspiration, language, code license
# TODO languages: ? at the end sort
# TODO too many spans, especially for text (maybe just plain text), also text with URLs inside is difficult (but why)
# TODO keywords: content, multiplayer replace by icons (open, commercial (dollar signs))
# TODO most people only come to the main page, put more information there (direct links to genres, ...)
# TODO put more explanations on the category pages and the categories (number and short sentences)
@ -31,6 +39,13 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
# TODO games: platform icons and mature, state larger (but maybe not on mobile)
# TODO games: platform information larger printed, keyword tags underlined to indicate links
# TODO devs: icons mark as links (how? ask on ux.stackexchange.com?)
# TODO everywhere: better link replacements
# TODO game: first homepage link bold
# TODO statistics: better and more statistics with links where possible
# TODO statistics: with nice graphics (pie charts in SVG) with matplotlib
# TODO statistics: get it from common statistics generator
@ -89,6 +104,7 @@ import shutil
import math
import datetime
import time
import json
from functools import partial
from utils import osg, constants as c, utils, osg_statistics as stat, osg_parse
from jinja2 import Environment, FileSystemLoader
@ -172,7 +188,7 @@ non_game_category_names = {
'tool': 'Tools',
'framework': 'Frameworks',
'library': 'Libraries',
'game engine': 'Game Engine'
'game engine': 'Game Engines'
}
# we check the output html structure every time
@ -842,6 +858,37 @@ def add_screenshot_information(entries):
entry['screenshots'] = screenshots
def create_table_json_data(entries):
"""
We assume that everything including internal is setup correctly.
Columns are Title, Link (entry, first homepage), State, Essential Keywords, Language, License
:param entries:
:return:
"""
# create json structure
db = {'headings': ['Title', 'State', 'Tags', 'Platform', 'Language', 'License']}
data = []
for entry in entries:
title = '<a href="{}">{}</a> (<a href="{}">Entry</a>)'.format(entry['Home'][0], entry['Title'], url_to([], entry['href']))
state = ', '.join(entry['State'])
tags = entry['Keyword']
tags = [tag for tag in tags if tag in c.recommended_keywords]
tags = ', '.join(tags)
platform = entry.get('Platform', ['-'])
platform = ', '.join(platform)
language = ', '.join(entry['Code language'])
license = [x[-1] for x in entry['Code license']]
license = ', '.join(license)
data.append([title, state, tags, platform, language, license])
data.sort(key=lambda x: str.casefold(x[0]))
db['data'] = data
# write out
text = json.dumps(db, indent=1)
os.makedirs(c.web_data_path, exist_ok=True)
utils.write_text(os.path.join(c.web_data_path, 'entries.json'), text)
def generate(entries, inspirations, developers):
"""
Regenerates the whole static website given an already imported set of entries, inspirations and developers.
@ -874,6 +921,9 @@ def generate(entries, inspirations, developers):
# set external links up
add_license_links_to_entries(games)
# create entries.json for the table
create_table_json_data(entries)
# sort into categories
sorter = lambda item, category: category == item['letter']
games_by_alphabet = sort_into_categories(games, extended_alphabet, sorter)
@ -893,7 +943,9 @@ def generate(entries, inspirations, developers):
# base dictionary
base = {
'title': 'OSGL',
'creation-date': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M')
'creation-date': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M'),
'css': ['bulma.min.css', 'osgl.min.css'],
'js': ['osgl.js']
}
# copy css and js
@ -925,16 +977,18 @@ def generate(entries, inspirations, developers):
# index.html
base['active_nav'] = 'index'
index = {'subtitle': make_text('Contains information about {} open source games and {} frameworks/tools.'.format(len(games), len(non_games))) }
index = {'subtitle': make_text('Contains information about {} open source games and {} game engines/tools.'.format(len(games), len(non_games))) }
template = environment.get_template('index.jinja')
write(template.render(index=index), ['index.html'])
# contribute page
base['title'] = 'OSGL | Contributions'
base['active_nav'] = 'contribute'
template = environment.get_template('contribute.jinja')
write(template.render(), ['contribute.html'])
# statistics page in statistics folder
base['title'] = 'OSGL | Statistics'
base['url_to'] = partial(url_to, statistics_path)
base['active_nav'] = 'statistics'
@ -960,6 +1014,7 @@ def generate(entries, inspirations, developers):
write(template.render(data=data), statistics_index_path)
# non-games folder
base['title'] = 'OSGL | Game engines, frameworks, tools'
base['url_to'] = partial(url_to, non_games_path)
base['active_nav'] = 'frameworks'
@ -985,6 +1040,7 @@ def generate(entries, inspirations, developers):
write(template_listing_entries.render(listing=listing), non_games_path + ['{}.html'.format(keyword)])
# games folder
base['title'] = 'OSGL | Games | Alphabetical'
base['url_to'] = partial(url_to, games_path)
base['active_nav'] = 'games'
@ -999,7 +1055,7 @@ def generate(entries, inspirations, developers):
# generate games index
index = divide_in_three_columns_and_transform(games_by_alphabet, game_index)
index['title'] = make_text('Open source games')
index['subtitle'] = make_text('Alphabetical index of informations about {} games'.format(len(games)))
index['subtitle'] = [make_text('Alphabetical index of informations about {} games (or see the '.format(len(games))), make_url(['table.html'], 'table'), ')']
index['categories'] = extended_alphabet
index['category-names'] = extended_alphabet_names
index['category-icons'] = {}
@ -1009,10 +1065,11 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_index_path)
# genres
base['title'] = 'OSGL | Games | Genres'
base['active_nav'] = ['filter', 'genres']
index = divide_in_three_columns_and_transform(games_by_genre, game_index)
index['title'] = make_text('Open source games')
index['subtitle'] = make_text('Index by game genre')
index['subtitle'] = [make_text('Index by game genre (or see the '), make_url(['table.html'], 'table'), ')']
index['categories'] = genres
index['category-names'] = {k: make_text(k) for k in index['categories']}
index['category-icons'] = {k: make_icon(genre_icon_map[k]) for k in index['categories'] if k in genre_icon_map}
@ -1022,10 +1079,11 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_by_genres_path)
# games by language
base['title'] = 'OSGL | Games | Programming language'
base['active_nav'] = ['filter', 'code language']
index = divide_in_three_columns_and_transform(games_by_language, game_index)
index['title'] = 'Open source games and frameworks'
index['subtitle'] = make_text('Index by programming language')
index['subtitle'] = [make_text('Index by programming language (or see the '), make_url(['table.html'], 'table'), ')']
index['categories'] = c.known_languages
index['category-names'] = {k:k for k in index['categories']}
index['category-icons'] = {}
@ -1035,10 +1093,11 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_by_language_path)
# games by platform
base['title'] = 'OSGL | Games | Supported Platform'
base['active_nav'] = ['filter', 'platforms']
index = divide_in_three_columns_and_transform(games_by_platform, game_index)
index['title'] = 'Open source games and frameworks'
index['subtitle'] = make_text('Index by supported platform')
index['subtitle'] = [make_text('Index by supported platform (or see the '), make_url(['table.html'], 'table'), ')']
index['categories'] = c.valid_platforms + ('Unspecified',)
index['category-names'] = {k: make_text(k) for k in index['categories']}
index['category-icons'] = {k: make_icon(platform_icon_map[k]) for k in index['categories']}
@ -1049,6 +1108,7 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_by_platform_path)
# top 50 games
base['title'] = 'OSGL | Games | GitHub Top 50'
base['active_nav'] = ['filter', 'top50']
# there are no other games coming afterwards, can actually number them
for index, game in enumerate(top50_games):
@ -1061,6 +1121,7 @@ def generate(entries, inspirations, developers):
write(template_listing_entries.render(listing=listing), games_top50_path)
# inspirations folder
base['title'] = 'OSGL | Inspirational games'
base['url_to'] = partial(url_to, inspirations_path)
base['active_nav'] = 'inspirations'
@ -1088,6 +1149,7 @@ def generate(entries, inspirations, developers):
write(template_listing_inspirations.render(listing=listing), inspirations_path + ['{}.html'.format(letter.capitalize())])
# developers folder
base['title'] = 'OSGL | Games | Developers'
base['url_to'] = partial(url_to, developers_path)
base['active_nav'] = 'developers'
@ -1112,6 +1174,15 @@ def generate(entries, inspirations, developers):
index['category-infos'] = {}
write(template_categorical_index.render(index=index), developers_index_path)
# dynamic table (is in top level folder)
base['title'] = 'OSGL | Entries | Table'
base['url_to'] = partial(url_to, [])
base['css'].append('simple-datatables.css')
base['js'].append('simple-datatables.js')
base['active_nav'] = []
template = environment.get_template('table.jinja')
write(template.render(), ['table.html'])
if __name__ == "__main__":

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,29 @@
{% extends "base.jinja" %}
{% block content %}
<div class="container">
<div class="box">
<div class="block">
<p class="title is-4">Table</p>
<p class="subtitle is-6">Sortable and searchable.</p>
</div>
</div>
<table class="table is-narrow is-hoverable"></table>
</div>
<script>
fetch("data/entries.json").then(response => response.json()).then(data => {
let table = new simpleDatatables.DataTable(".table", {
perPage: 50,
perPageSelect: [30, 50, 100],
footer: true,
data: {
headings: data["headings"],
data: data["data"]
},
});
table.on('datatable.init', function(args) {
table.columns().sort(0);
});
})
</script>
{% endblock %}

View File

@ -42,6 +42,7 @@ def check_validity_backlog():
if r.is_redirect or r.history:
print('{} redirected to {}, {}'.format(url, r.url, r.history))
def create_toc(title, file, entries):
"""
@ -697,15 +698,6 @@ class EntriesMaintainer:
print('statistics updated')
def update_html(self):
"""
"""
if not self.entries:
print('entries not yet loaded')
return
print('HTML not updated')
def update_repos(self):
"""
export to json for local repository update of primary repos
@ -905,7 +897,6 @@ if __name__ == "__main__":
'Clean backlog': m.clean_backlog,
'Update Readme and TOCs': m.update_readme_tocs,
'Update statistics': m.update_statistics,
'Update HTML': m.update_html,
'Update repository list': m.update_repos,
'Special': m.special_ops,
'Complete run': m.complete_run

View File

@ -7,15 +7,17 @@ import configparser
# paths
root_path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir))
code_path = os.path.join(root_path, 'code')
web_template_path = os.path.join(code_path, 'html')
entries_path = os.path.join(root_path, 'entries')
tocs_path = os.path.join(entries_path, 'tocs')
code_path = os.path.join(root_path, 'code')
web_path = os.path.join(root_path, 'docs')
screenshots_path = os.path.join(entries_path, 'screenshots')
web_template_path = os.path.join(code_path, 'html')
web_path = os.path.join(root_path, 'docs')
web_css_path = os.path.join(web_path, 'css')
web_js_path = os.path.join(web_path, 'js')
web_screenshots_path = os.path.join(web_path, 'screenshots')
web_data_path = os.path.join(web_path, 'data')
private_properties_file = os.path.join(root_path, 'private.properties')
inspirations_file = os.path.join(root_path, 'inspirations.md')

View File

@ -657,9 +657,13 @@ def write_screenshots_overview(overview):
text = utils.read_text(c.screenshots_file)
text = text.split('\n# ')[0] + '\n'
for name, a in overview.items():
# write out each entry sorted by name
for name in sorted(overview.keys()):
a = overview[name]
t = '# {}\n\n'.format(name)
for id, ai in a.items():
# write out each line sorted by id
for id in sorted(a.keys()):
ai = a[id]
if ai[-1] is None:
ai = ai[:-1]
t += ' '.join(['{:02d}'.format(id)] + [str(x) for x in ai]) + '\n'