screenshot (github top 50) and statistics charts support in website output

game engines now counted as frameworks
few additions
This commit is contained in:
Trilarion
2021-10-08 13:34:01 +02:00
parent 32907d0498
commit 8486b618e1
133 changed files with 12015 additions and 23895 deletions

View File

@ -38,7 +38,7 @@
<a class="navbar-item{% if 'frameworks' in base['active_nav'] %} is-active{% endif %}" href="{{ base['url_to'](['frameworks', 'index.html']) }}">{{ macros.render_icon({'class':'wrench'}) }}<span>Frameworks/Tools</span></a>
<a class="navbar-item{% if 'developers' in base['active_nav'] %} is-active{% endif %}" href="{{ base['url_to'](['developers', 'index.html']) }}">{{ macros.render_icon({'class':'users'}) }}<span>Developers</span></a>
<a class="navbar-item{% if 'inspirations' in base['active_nav'] %} is-active{% endif %}" href="{{ base['url_to'](['inspirations', 'index.html']) }}">{{ macros.render_icon({'class':'bulb'}) }}<span>Inspirations</span></a>
<a class="navbar-item{% if 'statistics' in base['active_nav'] %} is-active{% endif %}" href="{{ base['url_to'](['statistics.html']) }}">{{ macros.render_icon({'class':'stats-dots'}) }}<span>Statistics</span></a>
<a class="navbar-item{% if 'statistics' in base['active_nav'] %} is-active{% endif %}" href="{{ base['url_to'](['statistics', 'index.html']) }}">{{ macros.render_icon({'class':'stats-dots'}) }}<span>Statistics</span></a>
<a class="navbar-item{% if 'contribute' in base['active_nav'] %} is-active{% endif %}" href="{{ base['url_to'](['contribute.html']) }}">{{ macros.render_icon({'class':'pencil'}) }}<span>Contribute</span></a>
<a class="navbar-item" href="https://github.com/Trilarion/opensourcegames">{{ macros.render_icon({'class':'github'}) }}<span>On GitHub</span></a>
</div>

View File

@ -10,6 +10,12 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
# 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)
# TODO text description automaticall generated from keywords, state, and technical informations for example: First-person action shooter written in C++, inspired by ... but inactive for 7 years.
# TODO statistics should have disclaimer (warning or info box) about accuracy with link to contribute guidelines
# TODO game move developer to technical information and maybe rename technical information to details
# TODO game engines should be sorted with frameworks/tools, not with games (they aren't games or are they?)
# TODO if the only change is a change in last updated, do not change it (we can probably check with git diff for it) or checksums
@ -23,6 +29,8 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
# TODO everywhere: singular, plural (game, entries, items)
# TODO games: platform icons and mature, state larger (but maybe not on mobile)
# 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
@ -74,6 +82,8 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
# TODO add dynamic table (what to include)
# TODO meta titles for all pages, make them nice because they appear in search results! (https://www.contentpowered.com/blog/good-ctr-search-console/)
import os
import shutil
import math
@ -100,12 +110,14 @@ games_path = ['games']
non_games_path = ['frameworks']
inspirations_path = ['inspirations']
developers_path = ['developers']
statistics_path = ['statistics']
# derived paths
games_index_path = games_path + ['index.html']
non_games_index_path = non_games_path + ['index.html']
inspirations_index_path = inspirations_path + ['index.html']
developers_index_path = developers_path + ['index.html']
statistics_index_path = statistics_path + ['index.html']
games_by_language_path = games_path + ['languages.html']
games_by_genres_path = games_path + ['genres.html']
@ -548,6 +560,20 @@ def make_tags(entries):
}
def make_detail(summary, detail):
"""
:param summary:
:param detail:
:return:
"""
return {
'type': 'detail',
'summary': summary,
'detail': detail
}
def developer_profile_link(link):
"""
Creates links to developer profiles.
@ -637,10 +663,12 @@ def create_keyword_tag(keyword):
else:
url = games_by_genres_path.copy()
url[-1] += '#{}'.format(keyword)
if keyword.capitalize() in genre_icon_map:
return make_url(url, [make_icon(genre_icon_map[keyword.capitalize()]), make_text(keyword)], '{} games'.format(keyword), 'tag is-info')
else:
return make_url(url, make_text(keyword), '{} games'.format(keyword), 'tag is-info')
# TODO are icons looking good in the keyword tags (I somehow doubt it), maybe put them separately somewhere?
#if keyword.capitalize() in genre_icon_map:
# return make_url(url, [make_icon(genre_icon_map[keyword.capitalize()]), make_text(keyword)], '{} games'.format(keyword), 'tag is-info')
#else:
# return make_url(url, make_text(keyword), '{} games'.format(keyword), 'tag is-info')
return make_url(url, make_text(keyword), '{} games'.format(keyword), 'tag is-info')
else:
return make_text(keyword, 'tag is-light')
@ -698,10 +726,7 @@ def convert_entries(entries, inspirations, developers):
if field == 'Inspiration':
e = [make_url(inspirations_references[x], make_text(x)) for x in e]
elif field == 'Developer':
if len(e) > 10: # many devs, print smaller
e = [make_url(developer_references[x], make_text(x, 'is-size-7')) for x in e]
else:
e = [make_url(developer_references[x], make_text(x)) for x in e]
e = [make_url(developer_references[x], make_text(x)) for x in e]
elif field in c.url_fields:
e = [make_url(x, shortcut_url(x, name)) for x in e]
else:
@ -777,7 +802,7 @@ def get_top50_games(games):
for game in games:
# get stars of repositories
stars = 0
for repo in game.get('Code repository', []):
for repo in game.get('Code repository', [])[:1]: # take at most one
if isinstance(repo, osg_parse.Value):
for c in repo.comment.split(','):
c = c.strip()
@ -798,6 +823,25 @@ def get_top50_games(games):
return top50_games
def add_screenshot_information(entries):
"""
:param entries:
:return:
"""
# read screenshot information
overview = osg.read_screenshots_overview()
# iterate over entries
for entry in entries:
# get screenshots entry
name = osg.canonical_name(entry['Title']) # TODO should be stored upon loading I guess
screenshots = overview.get(name, {})
screenshots = [{'width': s[0], 'height': s[1], 'url': s[2], 'file': ['screenshots', '{}_{:02d}.jpg'.format(name, id)]} for id, s in screenshots.items()]
if screenshots: # TODO url None should be treated here or in the jinja template
entry['screenshots'] = screenshots
def generate(entries, inspirations, developers):
"""
Regenerates the whole static website given an already imported set of entries, inspirations and developers.
@ -856,6 +900,12 @@ def generate(entries, inspirations, developers):
utils.copy_tree(os.path.join(c.web_template_path, 'css'), c.web_css_path)
utils.copy_tree(os.path.join(c.web_template_path, 'js'), c.web_js_path)
# copy screenshots path
files = [file for file in os.listdir(c.screenshots_path) if file.endswith('.jpg')]
os.makedirs(c.web_screenshots_path, exist_ok=True)
for file in files:
shutil.copyfile(os.path.join(c.screenshots_path, file), os.path.join(c.web_screenshots_path, file))
# collage_image and google search console token and favicon.svg
for file in ('collage_games.jpg', 'google1f8a3863114cbcb3.html', 'favicon.svg'):
shutil.copyfile(os.path.join(c.web_template_path, file), os.path.join(c.web_path, file))
@ -884,7 +934,8 @@ def generate(entries, inspirations, developers):
template = environment.get_template('contribute.jinja')
write(template.render(), ['contribute.html'])
# statistics page
# statistics page in statistics folder
base['url_to'] = partial(url_to, statistics_path)
base['active_nav'] = 'statistics'
# statistics preparation
@ -896,16 +947,17 @@ def generate(entries, inspirations, developers):
# build-systems
build_systems_stat = stat.get_build_systems(entries)
# TODO replace all entries < 10 with "others"
# TODO make Pie chart out of it
build_systems_stat = stat.truncate_stats(build_systems_stat, 10)
stat.export_pie_chart([stat for stat in build_systems_stat if stat[0] != 'N/A'], os.path.join(c.web_path, 'statistics', 'build_systems.svg'))
section = {
'title': 'Build system',
'items': ['{} ({})'.format(*item) for item in build_systems_stat]
'items': ['{} ({})'.format(*item) for item in build_systems_stat],
'chart': statistics_path + ['build_systems.svg']
}
data['sections'].append(section)
# render and write statistics page
write(template.render(data=data), ['statistics.html'])
write(template.render(data=data), statistics_index_path)
# non-games folder
base['url_to'] = partial(url_to, non_games_path)
@ -998,6 +1050,9 @@ def generate(entries, inspirations, developers):
# top 50 games
base['active_nav'] = ['filter', 'top50']
# there are no other games coming afterwards, can actually number them
for index, game in enumerate(top50_games):
game['name'] = '{}. '.format(index+1) + game['name']
listing = {
'title': 'GitHub Stars Top 50',
'subtitle': '50 highest rated (by stars on Github) open source games in the database',
@ -1066,11 +1121,14 @@ if __name__ == "__main__":
print('clean current static website')
utils.recreate_directory(c.web_path)
# load entries, inspirations and developers and sort them
# load entries, inspirations and developers and sort them alphabetically
print('load entries, inspirations and developers')
entries = osg.read_entries()
entries.sort(key=lambda x: str.casefold(x['Title']))
# add screenshot information
add_screenshot_information(entries)
inspirations = osg.read_inspirations()
inspirations = list(inspirations.values())
inspirations.sort(key=lambda x: str.casefold(x['Name']))

View File

@ -23,15 +23,27 @@
</div>
{#- important fields in a certain order -#}
<div class="block">
{%- for field in ('homepage', 'media', 'inspiration', 'download', 'play online', 'developer') -%}
{%- for field in ('homepage', 'media', 'inspiration', 'download', 'play online') -%}
{%- if field in item -%}{{ macros.render_element(item[field]) }}<br>{%- endif -%}
{%- endfor -%}
</div>
{#- screenshots if available -#}
{%- if 'screenshots' in item%}<nav class="level">
{%- for screenshot in item['screenshots'] -%}
<div class="level-item"><a href="{{ screenshot['url'] }}"><img src="{{ base['url_to'](screenshot['file']) }}" width="{{ screenshot['width'] }}" height="{{ screenshot['height'] }}" alt=""></a></div>
{%- endfor -%}
</nav>{% endif -%}
{#- technical fields -#}
<div class="block is-size-7">
<span class="has-text-weight-semibold">Technical information</span><br>
{%- for field in ('code language', 'code license', 'code repository', 'code dependency', 'assets license', 'build system') -%}
{%- if field in item -%}{{ macros.render_element(item[field]) }}<br>{%- endif -%}
<span class="has-text-weight-semibold">Details</span><br>
{%- for field in ('code language', 'code license', 'code repository', 'code dependency', 'assets license', 'build system', 'developer') -%}
{%- if field in item -%}
{%- if item[field][1]['entries']|length > 10 -%}
<details><summary>{{ macros.render_element(item[field][0]) }} ({{ item[field][1]['entries']|length }})</summary>{{ macros.render_element(item[field][1]) }}</details>
{%- else -%}
{{ macros.render_element(item[field]) }}
{%- endif -%}
<br>{%- endif -%}
{%- endfor -%}
</div>
{#- improve, raw -#}

View File

@ -55,6 +55,7 @@ def download_images():
except:
pass
def downsize_images():
scale_factor = 10
for file in os.listdir(download_path):

View File

@ -12,6 +12,7 @@
<section class="section">
<div class="container">
<p id="" class="title is-4">{{ section['title'] }}</p>
{% if 'chart' in section %}<img src="{{ base['url_to'](section['chart']) }}">{% endif %}
<ul>
{% for item in section['items'] -%}
<li>{{ item }}</li>