選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 

112 行
3.6 KiB

  1. import os
  2. import sys
  3. from typing import Iterable
  4. from jinja2 import Environment, FileSystemLoader, Template, exceptions
  5. import config as cfg
  6. from . import app_root_dir, doc_root_dir, resource_dir, template_dir, base_dir
  7. _usage = "Usage: generate.py <provider>"
  8. def load_tmpl(tmpl: str) -> Template:
  9. env = Environment(loader=FileSystemLoader(template_dir()))
  10. env.filters["up_or_title"] = up_or_title
  11. return env.get_template(tmpl)
  12. def up_or_title(pvd: str, s: str) -> str:
  13. if s in cfg.UPPER_WORDS.get(pvd, ()):
  14. return s.upper()
  15. if s in cfg.TITLE_WORDS.get(pvd, {}):
  16. return cfg.TITLE_WORDS[pvd][s]
  17. return s.title()
  18. def gen_classes(pvd: str, typ: str, paths: Iterable[str]) -> str:
  19. """Generate all service node classes based on resources paths with class templates."""
  20. tmpl = load_tmpl(cfg.TMPL_MODULE)
  21. # TODO: extract the gen class metas for sharing
  22. # TODO: independent function for generating all pvd/typ/paths pairs
  23. def _gen_class_meta(path: str) -> dict:
  24. base = os.path.splitext(path)[0]
  25. name = "".join([up_or_title(pvd, s) for s in base.split("-")])
  26. return {"name": name, "icon": path}
  27. metas = map(_gen_class_meta, paths)
  28. aliases = cfg.ALIASES[pvd][typ] if typ in cfg.ALIASES[pvd] else {}
  29. return tmpl.render(pvd=pvd, typ=typ, metas=metas, aliases=aliases)
  30. def gen_apidoc(pvd: str, typ_paths: dict) -> str:
  31. try:
  32. default_tmp = cfg.TMPL_APIDOC.split('.')
  33. tmpl_file = f"{default_tmp[0]}_{pvd}.{default_tmp[1]}"
  34. tmpl = load_tmpl(tmpl_file)
  35. except exceptions.TemplateNotFound:
  36. tmpl = load_tmpl(cfg.TMPL_APIDOC)
  37. # TODO: remove
  38. def _gen_class_name(path: str) -> str:
  39. base = os.path.splitext(path)[0]
  40. name = "".join([up_or_title(pvd, s) for s in base.split("-")])
  41. return name
  42. typ_classes = {}
  43. for typ, (paths, resource_root) in sorted(typ_paths.items()):
  44. typ_classes[typ] = []
  45. for path in paths:
  46. name = _gen_class_name(path)
  47. resource_path = os.path.join(resource_root, path)
  48. alias = cfg.ALIASES[pvd].get(typ, {}).get(name)
  49. typ_classes[typ].append({"name": name, "alias": alias, "resource_path": resource_path})
  50. return tmpl.render(pvd=pvd, typ_classes=typ_classes)
  51. def make_module(pvd: str, typ: str, classes: str) -> None:
  52. """Create a module file"""
  53. mod_path = os.path.join(app_root_dir(pvd), f"{typ}.py")
  54. with open(mod_path, "w+") as f:
  55. f.write(classes)
  56. def make_apidoc(pvd: str, content: str) -> None:
  57. """Create an api documentation file"""
  58. mod_path = os.path.join(doc_root_dir(), f"{pvd}.md")
  59. with open(mod_path, "w+") as f:
  60. f.write(content)
  61. def generate(pvd: str) -> None:
  62. """Generates a service node classes."""
  63. typ_paths = {}
  64. base = base_dir()
  65. for root, _, files in os.walk(resource_dir(pvd)):
  66. # Extract the names and paths from resources.
  67. files.sort()
  68. pngs = list(filter(lambda f: f.endswith(".png"), files))
  69. paths = list(filter(lambda f: "rounded" not in f, pngs))
  70. # Skip the top-root directory.
  71. typ = os.path.basename(root)
  72. if typ == pvd:
  73. continue
  74. resource_root = os.path.relpath(root, base)
  75. classes = gen_classes(pvd, typ, paths)
  76. make_module(pvd, typ, classes)
  77. typ_paths[typ] = (paths, resource_root)
  78. # Build API documentation
  79. apidoc = gen_apidoc(pvd, typ_paths)
  80. make_apidoc(pvd, apidoc)
  81. if __name__ == "__main__":
  82. pvd = sys.argv[1]
  83. if pvd not in cfg.PROVIDERS:
  84. sys.exit()
  85. generate(pvd)