build_class_graphical.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/env python3
  2. ############################################################################
  3. #
  4. # MODULE: build_class_graphical
  5. # AUTHOR(S): Vaclav Petras <wenzeslaus gmail com>
  6. # PURPOSE: Build page with modules per family/class/category with images
  7. # COPYRIGHT: (C) 2015 by Vaclav Petras and the GRASS Development Team
  8. #
  9. # This program is free software under the GNU General Public
  10. # License (>=v2). Read the file COPYING that comes with GRASS
  11. # for details.
  12. #
  13. #############################################################################
  14. import sys
  15. import os
  16. import fnmatch
  17. # from build_html import *
  18. from build_html import (
  19. default_year,
  20. header1_tmpl,
  21. grass_version,
  22. modclass_intro_tmpl,
  23. to_title,
  24. html_files,
  25. check_for_desc_override,
  26. get_desc,
  27. write_html_footer,
  28. replace_file,
  29. )
  30. header_graphical_index_tmpl = """\
  31. <link rel="stylesheet" href="grassdocs.css" type="text/css">
  32. <style>
  33. .img-list {
  34. margin: 0;
  35. padding: 0;
  36. list-style-type: none;
  37. }
  38. .img-list li {
  39. padding: 5px;
  40. overflow: auto;
  41. }
  42. .img-list li:hover {
  43. background-color: #eee;
  44. }
  45. .img-list li a {
  46. color: initial;
  47. text-decoration: none;
  48. display: block;
  49. }
  50. .img-list li img {
  51. width: 10%;
  52. float: left;
  53. margin: 0 15px 0 0;
  54. background: white;
  55. object-fit: scale-down;
  56. }
  57. .img-list li img.default-img {
  58. max-height: 5ex;
  59. }
  60. .img-list li .desc {
  61. margin: 0px;
  62. }
  63. .img-list li .name {
  64. margin: 5px;
  65. display: block;
  66. color: #409940;
  67. font-weight: bold;
  68. font-style: italic;
  69. }
  70. </style>
  71. </head>
  72. <body style="width: 99%">
  73. <div id="container">
  74. <a href="index.html"><img src="grass_logo.png" alt="GRASS logo"></a>
  75. <hr class="header">
  76. <h2>Graphical index of GRASS GIS modules</h2>
  77. """
  78. def file_matches(filename, patterns):
  79. for pattern in patterns:
  80. if fnmatch.fnmatch(filename, pattern):
  81. return True
  82. return False
  83. def starts_with_module(string, module):
  84. # not solving:
  85. # module = module.replace('wxGUI.', 'g.gui.')
  86. # TODO: matches g.mapsets images for g.mapset and d.rast.num for d.rast
  87. if string.startswith(module.replace(".", "_")):
  88. return True
  89. if string.startswith(module.replace(".", "")):
  90. return True
  91. if string.startswith(module):
  92. return True
  93. return False
  94. def get_module_image(module, images):
  95. candidates = []
  96. for image in images:
  97. if starts_with_module(image, module):
  98. candidates.append(image)
  99. if len(candidates) == 1:
  100. # matches g.mapsets images for g.mapset and d.rast.num for d.rast
  101. return candidates[0]
  102. if not candidates:
  103. return None
  104. for image in candidates:
  105. basename, unused = image.rsplit(".", 1)
  106. if basename == module.replace(".", "_"):
  107. return image
  108. if basename == module.replace(".", ""):
  109. return image
  110. if basename == module:
  111. return image
  112. return sorted(candidates, key=len)[0]
  113. def generate_page_for_category(
  114. short_family, module_family, imgs, year, skip_no_image=False
  115. ):
  116. filename = module_family + "_graphical.html"
  117. output = open(filename + ".tmp", "w")
  118. output.write(
  119. header1_tmpl.substitute(
  120. title="GRASS GIS %s Reference " "Manual: Graphical index" % grass_version
  121. )
  122. )
  123. output.write(header_graphical_index_tmpl)
  124. if module_family.lower() not in ["general", "postscript"]:
  125. if module_family == "raster3d":
  126. # covert keyword to nice form
  127. module_family = "3D raster"
  128. output.write(
  129. modclass_intro_tmpl.substitute(
  130. modclass=module_family, modclass_lower=module_family.lower()
  131. )
  132. )
  133. if module_family == "wxGUI":
  134. output.write("<h3>wxGUI components:</h3>")
  135. elif module_family == "guimodules":
  136. output.write("<h3>g.gui.* modules:</h3>")
  137. else:
  138. output.write("<h3>{0} modules:</h3>".format(to_title(module_family)))
  139. output.write('<ul class="img-list">')
  140. # for all modules:
  141. for cmd in html_files(short_family, ignore_gui=False):
  142. basename = os.path.splitext(cmd)[0]
  143. desc = check_for_desc_override(basename)
  144. if desc is None:
  145. desc = get_desc(cmd)
  146. img = get_module_image(basename, imgs)
  147. img_class = "linkimg"
  148. if skip_no_image and not img:
  149. continue
  150. elif not img:
  151. img = "grass_logo.png"
  152. img_class = "default-img"
  153. if basename.startswith("wxGUI"):
  154. basename = basename.replace(".", " ")
  155. output.write(
  156. "<li>"
  157. '<a href="{html}">'
  158. '<img class="{img_class}" src="{img}">'
  159. '<span class="name">{name}</span> '
  160. '<span class="desc">{desc}</span>'
  161. "</a>"
  162. "</li>".format(
  163. html=cmd, img=img, name=basename, desc=desc, img_class=img_class
  164. )
  165. )
  166. output.write("</ul>")
  167. write_html_footer(output, "index.html", year)
  168. output.close()
  169. replace_file(filename)
  170. # TODO: dependencies in makefile for this have to be fixed
  171. # TODO: there is a potential overlap with other scripts (-> refactoring)
  172. def main():
  173. year = default_year
  174. html_dir = sys.argv[1]
  175. os.chdir(html_dir)
  176. img_extensions = ["png", "jpg", "gif"]
  177. img_patterns = ["*." + extension for extension in img_extensions]
  178. imgs = []
  179. for filename in sorted(os.listdir(html_dir)):
  180. if file_matches(filename, img_patterns):
  181. imgs.append(filename)
  182. # using term family
  183. # category has its meaning in GRASS already
  184. # class has its meaning in Python, plus it is a synonym for category
  185. # TODO: what would be user friendly is unclear
  186. families = [
  187. ("d", "display"),
  188. ("db", "database"),
  189. ("g", "general"),
  190. ("i", "imagery"),
  191. ("m", "miscellaneous"),
  192. ("ps", "postscript"),
  193. ("r", "raster"),
  194. ("r3", "raster3d"),
  195. ("t", "temporal"),
  196. ("v", "vector"),
  197. ("wxGUI", "wxGUI"),
  198. ("g.gui", "guimodules"),
  199. ]
  200. # partial compatibility with build_class.py
  201. # first arg is dist html dir but the 3 other are like first 3 there
  202. if len(sys.argv) > 2:
  203. short_family = sys.argv[2]
  204. module_family = sys.argv[3]
  205. if len(sys.argv) > 4:
  206. year = sys.argv[4]
  207. for short_family, module_family in families:
  208. generate_page_for_category(
  209. short_family, module_family, imgs, year=year, skip_no_image=False
  210. )
  211. if __name__ == "__main__":
  212. main()