build_class_graphical.py 6.5 KB

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