render.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #!/usr/bin/env python
  2. import html
  3. import json
  4. import os, os.path as op
  5. import shutil
  6. import functools
  7. import argparse
  8. import logging
  9. from pathlib import Path
  10. import markupsafe
  11. import unidecode
  12. import jinja2
  13. from pykwalify_webform.renderer import Renderer
  14. from slugify import slugify
  15. from yaml import safe_load
  16. import _ext
  17. HERE = Path(__file__).parent
  18. logging.basicConfig(level=logging.INFO)
  19. log = logging.getLogger()
  20. DIR = op.dirname(__file__)
  21. class Site:
  22. pass
  23. @functools.lru_cache(10)
  24. def env():
  25. return jinja2.Environment(loader=jinja2.FileSystemLoader(DIR))
  26. @functools.lru_cache(10)
  27. def ctx():
  28. site = Site()
  29. _ext.parse_data(site)
  30. return site
  31. def render_to(src, dst, **ctx):
  32. t = env().get_template(src)
  33. log.info(f'Rendering {src} -> {dst}')
  34. res = t.render(**ctx)
  35. os.makedirs(op.dirname(dst), exist_ok=True)
  36. with open(dst, 'w', encoding='utf-8') as f:
  37. f.write(res)
  38. def copy_to(src, dst):
  39. log.info(f'Copying {src} -> {dst}')
  40. shutil.copytree(src, dst)
  41. def render_all(target):
  42. if op.exists(target):
  43. shutil.rmtree(target)
  44. copy_to('static', target + '/static')
  45. site = ctx()
  46. render_to('index.html', f'{target}/index.html', site=site)
  47. updated = max(game['updated'] for names, meta, game in site.new_games.values())
  48. render_to('feed.xml', f'{target}/feed.xml', site=site, updated=updated)
  49. for game in site.games:
  50. render_to(
  51. 'game.html',
  52. f'{target}/{game.slug}/index.html',
  53. site=site,
  54. game=game,
  55. title=f"{game.names[0]} clones - OSGC",
  56. description=f"List of open source clones and remakes for {game.names[0]}"
  57. )
  58. render_data(f"{target}/{game.slug}/data.json", game.item)
  59. # Render data for edit game/clone forms
  60. clones = {clone["name"]: clone for game in site.games for clone in game.clones}
  61. for name, clone in clones.items():
  62. render_data(f"{target}/_clones/{slugify(name)}.json", clone)
  63. def normalize(text):
  64. if not text:
  65. return ''
  66. return html.escape(unidecode.unidecode(text.lower()))
  67. def render_game_form(schema: str, out_path: str, form_name: str, value=None):
  68. log.info(f"Rendering game form {schema=} -> {out_path}")
  69. with open(schema) as f:
  70. schemata = safe_load(f)
  71. renderer = Renderer(schemata, HERE / "templates/forms")
  72. os.makedirs(os.path.dirname(out_path), exist_ok=True)
  73. with open(out_path, "w") as f:
  74. f.write(renderer.render("", name=form_name, value=value, static_url="/_add_form"))
  75. def render_data(out_path: str, value):
  76. Path(out_path).parent.mkdir(parents=True, exist_ok=True)
  77. with open(out_path, "w") as f:
  78. json.dump(value, f, indent=2, default=str)
  79. def main():
  80. parser = argparse.ArgumentParser(description='Render OSGC')
  81. parser.add_argument('-d', '--dest', default='_build')
  82. args = parser.parse_args()
  83. env().filters['normalize'] = normalize
  84. env().filters['slugify'] = slugify
  85. env().filters['e'] = markupsafe.escape
  86. render_all(args.dest)
  87. # Render add game forms
  88. render_game_form("schema/games.yaml", f"{args.dest}/add_game.html", "Add Game")
  89. render_game_form("schema/originals.yaml", f"{args.dest}/add_original.html", "Add Original")
  90. # Copy static files
  91. shutil.copytree(str(HERE / "templates/forms/static"), f"{args.dest}/_add_form")
  92. if __name__ == '__main__':
  93. main()