25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

generate.py 3.3 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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
  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 in sorted(typ_paths.items()):
  44. typ_classes[typ] = []
  45. for name in map(_gen_class_name, paths):
  46. alias = cfg.ALIASES[pvd].get(typ, {}).get(name)
  47. typ_classes[typ].append({"name": name, "alias": alias})
  48. return tmpl.render(pvd=pvd, typ_classes=typ_classes)
  49. def make_module(pvd: str, typ: str, classes: str) -> None:
  50. """Create a module file"""
  51. mod_path = os.path.join(app_root_dir(pvd), f"{typ}.py")
  52. with open(mod_path, "w+") as f:
  53. f.write(classes)
  54. def make_apidoc(pvd: str, content: str) -> None:
  55. """Create an api documentation file"""
  56. mod_path = os.path.join(doc_root_dir(), f"{pvd}.md")
  57. with open(mod_path, "w+") as f:
  58. f.write(content)
  59. def generate(pvd: str) -> None:
  60. """Generates a service node classes."""
  61. typ_paths = {}
  62. for root, _, files in os.walk(resource_dir(pvd)):
  63. # Extract the names and paths from resources.
  64. files.sort()
  65. pngs = list(filter(lambda f: f.endswith(".png"), files))
  66. paths = list(filter(lambda f: "rounded" not in f, pngs))
  67. # Skip the top-root directory.
  68. typ = os.path.basename(root)
  69. if typ == pvd:
  70. continue
  71. classes = gen_classes(pvd, typ, paths)
  72. make_module(pvd, typ, classes)
  73. typ_paths[typ] = paths
  74. # Build API documentation
  75. apidoc = gen_apidoc(pvd, typ_paths)
  76. make_apidoc(pvd, apidoc)
  77. if __name__ == "__main__":
  78. pvd = sys.argv[1]
  79. if pvd not in cfg.PROVIDERS:
  80. sys.exit()
  81. generate(pvd)