build_class_graphical.py 6.6 KB

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