PageRenderTime 92ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/todo/todo.py

https://bitbucket.org/javierbuilder/gmate
Python | 383 lines | 321 code | 19 blank | 43 comment | 15 complexity | 8fe73cdb53dda5c2571a99009d2a60ea MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2007 - Alexandre da Silva
  4. #
  5. # Inspired in Nando Vieira's todo.rb source code
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2, or (at your option)
  10. # any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. #
  21. import os, sys, operator
  22. from stat import *
  23. from string import Template
  24. import re
  25. def parse_directory(root):
  26. home_folder = os.path.expanduser('~')
  27. # TODO: Look first for a config file present in /etc to facility configuration
  28. # Config FileName
  29. config_file = os.path.join(os.path.dirname(__file__), "todo.conf")
  30. # Configs read regular expression
  31. cfg_rx = re.compile(r"(ALLOWED_EXTENSIONS|SKIPED_DIRS|KNOWN_MARKS|SKIPED_FILES|SHOW_EMPTY_MARKS|REQUIRE_COLON|MARK_COLORS)=+(.*?)$")
  32. # Get Configuration Info
  33. cfg_file = open(config_file,'r')
  34. cfg_data = cfg_file.read().split('\n')
  35. configs = {'ALLOWED_EXTENSIONS':'','SKIPED_DIRS':'','KNOWN_MARKS':'',\
  36. 'SKIPED_FILES':'','SHOW_EMPTY_MARKS':'0','REQUIRE_COLON':'1','MARK_COLORS': ''}
  37. for cfg_line in cfg_data:
  38. cfg_match = cfg_rx.search(cfg_line)
  39. if cfg_match:
  40. configs[cfg_match.group(1)] = cfg_match.group(2)
  41. def make_regex(config_str):
  42. return "|".join([re.escape(k) for k in configs[config_str].split(';')])
  43. allowed_extensions_regex = make_regex('ALLOWED_EXTENSIONS')
  44. skiped_dirs_regex = make_regex('SKIPED_DIRS')
  45. known_marks_regex = make_regex('KNOWN_MARKS')
  46. skiped_files_regex = make_regex('SKIPED_FILES')
  47. known_marks_list = known_marks_regex.split('|')
  48. # Initial Setup
  49. allowed_types = re.compile(r'.*\.\b(%s)\b$' % allowed_extensions_regex)
  50. skiped_dirs = re.compile(r'.*(%s)$' % skiped_dirs_regex)
  51. # Enable os disable colons
  52. if configs["REQUIRE_COLON"] == "1":
  53. known_marks = re.compile(r'\b(%s)\b\s?: +(.*?)$' % known_marks_regex)
  54. else:
  55. known_marks = re.compile(r'\b(%s)\b\s?:? +(.*?)$' % known_marks_regex)
  56. skiped_files = re.compile(r"("+skiped_files_regex+")$")
  57. total_marks = 0
  58. # Helper Functions
  59. def file_link(file, line=0):
  60. return "gedit:///%s?line=%d" % (file,line-1)
  61. # Escape possible tags from comments as HTML
  62. def escape(str_):
  63. lt = re.compile(r'<')
  64. gt = re.compile(r'>')
  65. return lt.sub("&lt;",gt.sub("&gt;",str_))
  66. # Todo Header image pattern
  67. def todo_header():
  68. return "file://" + os.path.join(os.path.dirname(__file__), "todo_header.png")
  69. # Todo Gear Image
  70. def todo_gears():
  71. return "file://" + os.path.join(os.path.dirname(__file__), "todo_gears.png")
  72. # Initialize the values list
  73. values = []
  74. # Markup Label Counter
  75. labels = {}
  76. for label in known_marks_list:
  77. labels[label]=0
  78. # walk over directory tree
  79. def walktree(top, callback):
  80. '''recursively descend the directory tree rooted at top,
  81. calling the callback function for each regular file'''
  82. for f in os.listdir(top):
  83. pathname = os.path.join(top, f)
  84. try:
  85. mode = os.stat(pathname)[ST_MODE]
  86. if S_ISDIR(mode):
  87. # It's a directory, recurse into it
  88. if not skiped_dirs.match(pathname):
  89. walktree(pathname, callback)
  90. elif S_ISREG(mode):
  91. # It's a file, call the callback function
  92. if not skiped_files.match(pathname):
  93. callback(pathname)
  94. else:
  95. # Unknown file type, pass
  96. pass
  97. except OSError:
  98. continue
  99. # Test File Callback function
  100. def test_file(file):
  101. """ Parse the file passed as argument searching for TODO Tags"""
  102. if allowed_types.match(file):
  103. try:
  104. file_search = open(file, 'r')
  105. except IOError:
  106. sys.exit(2)
  107. data = file_search.read()
  108. data = data.split('\n')
  109. # Line Number
  110. ln = 0
  111. for line in data:
  112. ln = ln + 1
  113. a_match = known_marks.search(line)
  114. if (a_match):
  115. pt, fl = os.path.split(file)
  116. labels[a_match.group(1)] += 1
  117. result = [file,fl,ln,a_match.group(1),a_match.group(2)]
  118. values.append(result)
  119. # Search Directories for files matching
  120. walktree(root, test_file)
  121. html = '<div id="todo_list">\n'
  122. # Make the Menu
  123. menu = '<ul id="navigation">\n'
  124. for label in labels:
  125. total_marks += labels[label]
  126. if configs['SHOW_EMPTY_MARKS'] == '1' or labels[label]:
  127. menu += ' <li class="%s"><a href="#%s-title">%s</a>: %d</li>\n' % (label.lower(), label.lower(), label, labels[label])
  128. menu += '<li class="total">Total: %d</li></ul>\n' % total_marks
  129. table_pattern = Template(\
  130. """\
  131. <h2 id=\"${label}-title\">${labelU}</h2>
  132. <table id="${label}">
  133. <thead>
  134. <tr>
  135. <th class="file">File</th>
  136. <th class="comment">Comment</th>
  137. </tr>
  138. </thead>
  139. <tbody>
  140. """
  141. )
  142. tables = {}
  143. for label_ in known_marks_list:
  144. tables[label_]= table_pattern.substitute(dict(label=label_.lower(),labelU=label_.upper()))
  145. table_row_pattern = ' <tr class="%s"><td><a href="%s" title="%s">%s</a> <span>(%s)</span></td><td>%s</td>\n'
  146. def format_row(value_):
  147. return table_row_pattern % (css, file_link(value_[0], value_[2]), value_[0], value_[1], value_[2], value_[4])
  148. for ix, value in enumerate(sorted(values,key=operator.itemgetter(3))):
  149. css = 'odd'
  150. if ix % 2 == 0:
  151. css = 'even'
  152. for table_value in tables:
  153. if value[3] == table_value:
  154. tables[table_value] += format_row(value)
  155. for table_value in tables:
  156. tables[table_value] += ' </tbody></table>\n'
  157. html += menu
  158. for label in labels:
  159. if labels[label]:
  160. html += tables[label]
  161. html += ' <a href="#todo_list" id="toplink">↑ top</a>\n </div>'
  162. todo_links_css_pattern = \
  163. """
  164. #${label}-title {
  165. color: ${color};
  166. }
  167. li.${label} {
  168. background: ${color};
  169. }
  170. """
  171. todo_links_css = ''
  172. color_rx = re.compile(r'^(.*)(#[0-9a-fA-F]{6})$')
  173. todo_links_template = Template(todo_links_css_pattern)
  174. for markcolor in configs['MARK_COLORS'].split(';'):
  175. c_match = color_rx.search(markcolor)
  176. if c_match:
  177. mark,mcolor = c_match.group(1), c_match.group(2)
  178. todo_links_css += todo_links_template.substitute(label=mark.lower(),color=mcolor)
  179. # TODO: load this template pattern from a file.
  180. html_pattern = \
  181. """
  182. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  183. "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
  184. <html>
  185. <head>
  186. <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  187. <title>TODO-List</title>
  188. <style type="text/css">
  189. * {
  190. color: #333;
  191. }
  192. body {
  193. font-size: 12px;
  194. font-family: "bitstream vera sans mono", "sans-serif";
  195. padding: 0;
  196. margin: 0;
  197. width: 700px;
  198. height: 500px;
  199. }
  200. th {
  201. text-align: left;
  202. }
  203. td {
  204. vertical-align: top;
  205. }
  206. ${labelcss}
  207. th, a {
  208. color: #0D2681;
  209. }
  210. .odd td {
  211. background: #f0f0f0;
  212. }
  213. table {
  214. border-collapse: collapse;
  215. width: 650px;
  216. }
  217. td,th {
  218. padding: 3px;
  219. }
  220. th {
  221. border-bottom: 1px solid #999;
  222. }
  223. th.file {
  224. width: 30%;
  225. }
  226. #toplink {
  227. position: fixed;
  228. bottom: 10px;
  229. right: 40px;
  230. }
  231. h1 {
  232. color: #fff;
  233. padding: 20px 5px 18px 5px;
  234. margin: 0;
  235. }
  236. h2 {
  237. font-size: 16px;
  238. margin: 0 0 10px;
  239. padding-top: 30px;
  240. }
  241. #page {
  242. overflow: auto;
  243. height: 406px;
  244. padding: 0 15px 20px 15px;
  245. position: relative;
  246. }
  247. #root {
  248. position: absolute;
  249. top: 28px;
  250. right: 23px;
  251. color: #fff;
  252. }
  253. #navigation {
  254. margin: 0;
  255. padding: 0;
  256. border-left: 1px solid #000;
  257. }
  258. #navigation * {
  259. color: #fff;
  260. }
  261. li.total {
  262. background: #000000;
  263. font-weight: bold
  264. }
  265. #navigation li {
  266. float: left;
  267. list-style: none;
  268. text-align: center;
  269. padding: 7px 10px;
  270. margin: 0;
  271. border: 1px solid #000;
  272. border-left: none;
  273. font-weight: bold
  274. }
  275. #navigation:after {
  276. content: ".";
  277. display: block;
  278. height: 0;
  279. clear: both;
  280. visibility: hidden;
  281. }
  282. #todo_list {
  283. padding-top: 30px;
  284. }
  285. #container {
  286. position: relative;
  287. background: url(${todo_header}) repeat-x;
  288. }
  289. #gears {
  290. float : right;
  291. margin : 0 0 0 0;
  292. }
  293. </style>
  294. </head>
  295. <body>
  296. <div id="container">
  297. <img src="${todo_gears}" id="gears" />
  298. <h1>TODO List</h1>
  299. <p id="root">${root}</p>
  300. <div id="page">
  301. ${html}
  302. </div>
  303. </div>
  304. </body>
  305. </html>
  306. """
  307. markup = Template(html_pattern)
  308. markup_out = markup.substitute(todo_header=todo_header(), \
  309. todo_gears=todo_gears(),root=escape(root), html=html, \
  310. labelcss=todo_links_css)
  311. return markup_out