trac0.11用
@@ -0,0 +1,16 @@ | ||
1 | +from setuptools import find_packages, setup | |
2 | + | |
3 | +setup( | |
4 | + name='StepCounterPlugin', version='0.1.0', | |
5 | + author = "tacky21jp", | |
6 | + author_email = "tacky21jp@gmail.com", | |
7 | + description = "Step Counter for repository.", | |
8 | + license = "NEW BSD", | |
9 | + url = "http://d.hatena.ne.jp/tacky21jp/", | |
10 | + packages=find_packages(exclude=['*.tests*']), | |
11 | + package_data={'stepcounter': ['templates/*.html','htdocs/css/*.css']}, | |
12 | + | |
13 | + entry_points = {'trac.plugins': | |
14 | + ['stepcounter.StepCounter = stepcounter.StepCounter',],}, | |
15 | + | |
16 | +) |
@@ -0,0 +1,225 @@ | ||
1 | +''' | |
2 | +Created on 2010/09/12 | |
3 | + | |
4 | +@author: tacky21jp | |
5 | +''' | |
6 | +import re | |
7 | +from datetime import datetime | |
8 | + | |
9 | +from trac.core import * | |
10 | +from trac.web import IRequestHandler | |
11 | +from trac.env import IEnvironmentSetupParticipant | |
12 | +from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet, add_script, add_link | |
13 | +from trac.perm import IPermissionRequestor | |
14 | +from trac.mimeview.api import Context | |
15 | +from trac.resource import ResourceNotFound | |
16 | +from trac.util import sorted, embedded_numbers | |
17 | +from trac.config import Configuration | |
18 | + | |
19 | +from trac.versioncontrol.api import NoSuchChangeset, NoSuchNode | |
20 | +from trac.versioncontrol.web_ui.util import * | |
21 | + | |
22 | +from genshi.builder import tag | |
23 | +from stepcounter.StepCounterBuilder import * | |
24 | + | |
25 | +class StepCounterPlugin(Component): | |
26 | + implements(IEnvironmentSetupParticipant, | |
27 | + INavigationContributor, IRequestHandler, ITemplateProvider,IPermissionRequestor) | |
28 | + | |
29 | + counterBuilder = StepCounterBuilder() | |
30 | + | |
31 | + # INavigationContributor methods | |
32 | + def get_active_navigation_item(self, req): | |
33 | + return 'StepCounter' | |
34 | + | |
35 | + def get_navigation_items(self, req): | |
36 | + default_path = Configuration.get(self.config, 'stepcounter', 'default_path', default='/') | |
37 | + | |
38 | + yield ('mainnav', 'StepCounter', | |
39 | + tag.a('StepCounter', href=req.href.stepcounter(default_path))) | |
40 | + | |
41 | + # IEnvironmentSetupParticipant methods | |
42 | + def environment_created(self): | |
43 | + pass | |
44 | + | |
45 | + def environment_needs_upgrade(self, db): | |
46 | + return False | |
47 | + | |
48 | + def upgrade_environment(self, db): | |
49 | + pass | |
50 | + | |
51 | + # IRequestHandler methods | |
52 | + def match_request(self, req): | |
53 | + match = re.match(r'/(stepcounter)(/.*)?$', req.path_info) | |
54 | + if match: | |
55 | + mode, path = match.groups() | |
56 | + req.args['path'] = path or '/' | |
57 | + return True | |
58 | + | |
59 | + | |
60 | + def process_request(self, req): | |
61 | + req.perm.assert_permission('BROWSER_VIEW') | |
62 | + | |
63 | + path = req.args.get('path', '/') | |
64 | + rev = req.args.get('rev', None) | |
65 | + order = req.args.get('order', None) | |
66 | + desc = req.args.get('desc', None) | |
67 | + xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest' | |
68 | + commiter = req.args.get('commiter', None) | |
69 | + | |
70 | + repos = self.env.get_repository(req.authname) | |
71 | + | |
72 | + try: | |
73 | + if rev: | |
74 | + rev = repos.normalize_rev(rev) | |
75 | + rev_or_latest = rev or repos.youngest_rev | |
76 | + node = get_existing_node(req, repos, path, rev_or_latest) | |
77 | + except NoSuchChangeset, e: | |
78 | + raise ResourceNotFound(e.message, _('Invalid Changeset Number')) | |
79 | + | |
80 | + context = Context.from_request(req, 'source', path, rev_or_latest) | |
81 | + | |
82 | + path_links = self.get_counter_path_links(req.href, path, rev, order, desc) | |
83 | + if len(path_links) > 1: | |
84 | + try: | |
85 | + add_link(req, 'up', path_links[-2]['href'], _('Parent directory')) | |
86 | + except Exception,e: | |
87 | + pass | |
88 | + | |
89 | + dir = node.isdir and self.count_dir(req, repos, node, rev, commiter) | |
90 | + | |
91 | + if dir: | |
92 | + sum_step=0 | |
93 | + sum_comment=0 | |
94 | + for r in dir['counts']: | |
95 | + if r.step: | |
96 | + sum_step = sum_step + r.step | |
97 | + if r.commentStep: | |
98 | + sum_comment = sum_comment + r.commentStep | |
99 | + dir['sum_step']=sum_step | |
100 | + dir['sum_comment']=sum_comment | |
101 | + | |
102 | + data = { | |
103 | + 'context': context, | |
104 | + 'path': path, 'rev': node.rev, 'stickyrev': rev, | |
105 | + 'created_path': node.created_path, | |
106 | + 'created_rev': node.created_rev, | |
107 | + 'path_links': path_links, | |
108 | + 'dir':dir | |
109 | + } | |
110 | + | |
111 | + add_stylesheet(req, 'stepcounter/css/stepcounter.css') | |
112 | + | |
113 | + return 'stepcounter.html',data, None | |
114 | + | |
115 | + # ITemplateProvider methods | |
116 | + def get_templates_dirs(self): | |
117 | + from pkg_resources import resource_filename | |
118 | + return [resource_filename(__name__, 'templates')] | |
119 | + | |
120 | + def get_htdocs_dirs(self): | |
121 | + from pkg_resources import resource_filename | |
122 | + return [('stepcounter',resource_filename(__name__, 'htdocs'))] | |
123 | + | |
124 | + #IPermissionRequestor methods | |
125 | + def get_permission_actions(self): | |
126 | + return ['BROWSER_VIEW ','BROWSER_VIEW '] | |
127 | + | |
128 | + | |
129 | + # Internal methods | |
130 | + | |
131 | + def count_dir(self, req, repos, node, rev=None, commiter=None): | |
132 | + | |
133 | + # Entries metadata | |
134 | + class entry(object): | |
135 | + __slots__ = 'name rev kind isdir path content_length'.split() | |
136 | + def __init__(self, node): | |
137 | + for f in self.__slots__: | |
138 | + setattr(self, f, getattr(node, f)) | |
139 | + | |
140 | + entries = [entry(n) for n in node.get_entries()] | |
141 | + result = [] | |
142 | + count_result = [] | |
143 | + | |
144 | + changes = get_changes(repos, [i.rev for i in entries]) | |
145 | + | |
146 | + for e in entries: | |
147 | + if not(e.isdir): | |
148 | + _node = get_existing_node(req, repos, e.path, rev) | |
149 | + rev_or_latest = rev or repos.youngest_rev | |
150 | + if commiter is None or changes[e.rev] is None or (changes[e.rev] and commiter == changes[e.rev].author): | |
151 | + context = Context.from_request(req, 'source', e.path, rev_or_latest) | |
152 | + count = self.count_file(req,context,repos,_node,rev) | |
153 | + | |
154 | + if count: | |
155 | + result.append(e) | |
156 | + count_result.append(count) | |
157 | + for e in entries: | |
158 | + if e.isdir: | |
159 | + _node = get_existing_node(req, repos, e.path, rev) | |
160 | + rev_or_latest = rev or repos.youngest_rev | |
161 | + _dat = self.count_dir(req, repos, _node, rev_or_latest, commiter) | |
162 | + | |
163 | + result.extend(_dat['entries']) | |
164 | + count_result.extend(_dat['counts']) | |
165 | + | |
166 | + if rev: | |
167 | + newest = repos.get_changeset(rev).date | |
168 | + else: | |
169 | + newest = datetime.now(req.tz) | |
170 | + | |
171 | + # Ordering of entries | |
172 | + order = req.args.get('order', 'name').lower() | |
173 | + desc = req.args.has_key('desc') | |
174 | + | |
175 | + if order == 'date': | |
176 | + def file_order(a): | |
177 | + return changes[a.rev].date | |
178 | + elif order == 'size': | |
179 | + def file_order(a): | |
180 | + return (a.content_length, | |
181 | + embedded_numbers(a.name.lower())) | |
182 | + else: | |
183 | + def file_order(a): | |
184 | + return embedded_numbers(a.name.lower()) | |
185 | + | |
186 | + dir_order = desc and 1 or -1 | |
187 | + | |
188 | + def browse_order(a): | |
189 | + return a.isdir and dir_order or 0, file_order(a) | |
190 | + entries = sorted(entries, key=browse_order, reverse=desc) | |
191 | + | |
192 | + | |
193 | + add_script(req, 'common/js/expand_dir.js') | |
194 | + add_script(req, 'common/js/keyboard_nav.js') | |
195 | + | |
196 | + return {'order': order, 'desc': desc and 1 or None, | |
197 | + 'entries': result, 'changes': changes, 'counts':count_result, | |
198 | + } | |
199 | + | |
200 | + def count_file(self, req, context, repos, node, rev=None): | |
201 | + | |
202 | + result = None | |
203 | + | |
204 | + counter = self.counterBuilder.getCounter(node.path) | |
205 | + | |
206 | + if counter: | |
207 | + content = node.get_content() | |
208 | + result = counter.countContent(content.read()) | |
209 | + #self.log.debug('%s' % node.path) | |
210 | + else: | |
211 | + #result = StepCountResult('','',0,0,0) | |
212 | + result = None | |
213 | + return result | |
214 | + | |
215 | + def get_counter_path_links(self,href, fullpath, rev, order=None, desc=None): | |
216 | + links = [{'name': 'root', | |
217 | + 'href': href.stepcounter(rev=rev, order=order, desc=desc)}] | |
218 | + path = '' | |
219 | + for part in [p for p in fullpath.split('/') if p]: | |
220 | + path += part + '/' | |
221 | + links.append({ | |
222 | + 'name': part, | |
223 | + 'href': href.stepcounter(path, rev=rev, order=order, desc=desc) | |
224 | + }) | |
225 | + return links | |
\ No newline at end of file |
@@ -0,0 +1,67 @@ | ||
1 | +@charset "utf-8"; | |
2 | + | |
3 | +.message{ | |
4 | + padding:3px; | |
5 | + border: 2px solid green; | |
6 | + background-color:#66CC99; | |
7 | + text-align:left; | |
8 | +} | |
9 | + | |
10 | +.error{ | |
11 | + padding:3px; | |
12 | + border: 2px solid #DD0000; | |
13 | + background-color:#FFDDCC; | |
14 | + text-align:left; | |
15 | +} | |
16 | + | |
17 | +.list { | |
18 | + border-collapse:collapse; | |
19 | + border:solid 1px #BBBBBB; | |
20 | + margin:0px 2px 0px 2px; | |
21 | +} | |
22 | + | |
23 | +.list .odd { | |
24 | + background:#FFFFFF; | |
25 | +} | |
26 | + | |
27 | +.list .even { | |
28 | + background:#F4F4F4; | |
29 | +} | |
30 | + | |
31 | +.list .expired { | |
32 | + background:#FF8888; | |
33 | +} | |
34 | + | |
35 | +.list .warn { | |
36 | + background:#FFFF88; | |
37 | +} | |
38 | + | |
39 | +.list th { | |
40 | + background:url(../img/list_label.gif) repeat-x; | |
41 | + border-right:solid 1px #BBBBBB; | |
42 | + border-bottom:solid 1px #BBBBBB; | |
43 | + text-align:center; | |
44 | + color:#444444; | |
45 | + padding:2px 0px 2px 0px; | |
46 | + white-space: nowrap; | |
47 | +} | |
48 | + | |
49 | +.list tbody td { | |
50 | + border-right:solid 1px #BBBBBB; | |
51 | + border-bottom:solid 1px #BBBBBB; | |
52 | + color:#444444; | |
53 | + padding:3px 5px; | |
54 | + white-space: nowrap; | |
55 | +} | |
56 | + | |
57 | +.list input { | |
58 | + height:20px; | |
59 | + padding:0px 2px; | |
60 | + margin:0px auto; | |
61 | + white-space: nowrap; | |
62 | +} | |
63 | + | |
64 | +.count{ | |
65 | + text-align:right; | |
66 | +} | |
67 | + |
@@ -0,0 +1 @@ | ||
1 | +from StepCounter import * |
@@ -0,0 +1,443 @@ | ||
1 | +''' | |
2 | +Created on 2010/09/12 | |
3 | + | |
4 | +@author: tacky21jp | |
5 | +''' | |
6 | +class StepCounterBuilder: | |
7 | + | |
8 | + counter_map = {} | |
9 | + | |
10 | + def getCounter(self,fileName): | |
11 | + | |
12 | + if(fileName.endswith(".java")): | |
13 | + counter = None | |
14 | + if self.counter_map.has_key("Java"): | |
15 | + counter = self.counter_map["Java"] | |
16 | + else: | |
17 | + counter = self.createJavaCounter("Java") | |
18 | + self.counter_map["Java"] = counter | |
19 | + return counter | |
20 | + | |
21 | + elif (fileName.endswith(".jsp")): | |
22 | + counter = None | |
23 | + if self.counter_map.has_key("JSP"): | |
24 | + counter = self.counter_map["JSP"] | |
25 | + else: | |
26 | + counter = DefaultStepCounter(); | |
27 | + counter.addSingleLineComment("//") | |
28 | + counter.addMultiLineComment("/*","*/") | |
29 | + counter.addMultiLineComment("<%--","--%>") | |
30 | + counter.addMultiLineComment("<!--","-->") | |
31 | + counter.setFileType("JSP") | |
32 | + self.counter_map["JSP"] = counter | |
33 | + return counter | |
34 | + | |
35 | + elif (fileName.endswith(".js")): | |
36 | + counter = None | |
37 | + if self.counter_map.has_key("js"): | |
38 | + counter = self.counter_map["js"] | |
39 | + else: | |
40 | + counter = self.createJavaCounter("js") | |
41 | + self.counter_map["js"] = counter | |
42 | + return counter | |
43 | + | |
44 | + elif (fileName.endswith(".cpp") or fileName.endswith(".c")): | |
45 | + counter = None | |
46 | + if self.counter_map.has_key("C/C++"): | |
47 | + counter = self.counter_map["C/C++"] | |
48 | + else: | |
49 | + counter = self.createJavaCounter("C/C++") | |
50 | + self.counter_map["C/C++"] = counter | |
51 | + return counter | |
52 | + | |
53 | + elif (fileName.endswith(".h")): | |
54 | + counter = None | |
55 | + if self.counter_map.has_key("h"): | |
56 | + counter = self.counter_map["h"] | |
57 | + else: | |
58 | + counter = self.createJavaCounter("h") | |
59 | + self.counter_map["h"] = counter | |
60 | + return counter | |
61 | + | |
62 | + elif (fileName.endswith(".cs")): | |
63 | + counter = None | |
64 | + if self.counter_map.has_key("C#"): | |
65 | + counter = self.counter_map["C#"] | |
66 | + else: | |
67 | + counter = self.createJavaCounter("C#") | |
68 | + self.counter_map["C#"] = counter | |
69 | + return counter | |
70 | + | |
71 | + elif (fileName.endswith(".php") or fileName.endswith(".php3")): | |
72 | + counter = None | |
73 | + if self.counter_map.has_key("PHP"): | |
74 | + counter = self.counter_map["PHP"] | |
75 | + else: | |
76 | + counter = DefaultStepCounter() | |
77 | + counter.addSingleLineComment("//") | |
78 | + counter.addMultiLineComment("/*","*/") | |
79 | + counter.addMultiLineComment("<!--","-->") | |
80 | + counter.setFileType("PHP") | |
81 | + self.counter_map["PHP"] = counter | |
82 | + return counter | |
83 | + | |
84 | + elif (fileName.endswith(".asp") or fileName.endswith(".asa") or fileName.endswith(".aspx") or fileName.endswith(".asax") or fileName.endswith(".ascx")): | |
85 | + counter = None | |
86 | + if self.counter_map.has_key("ASP"): | |
87 | + counter = self.counter_map["ASP"] | |
88 | + else: | |
89 | + counter = DefaultStepCounter() | |
90 | + counter.addSingleLineComment("'") | |
91 | + counter.addSingleLineComment("//") | |
92 | + counter.addMultiLineComment("/*","*/") | |
93 | + counter.addMultiLineComment("<%--","--%>") | |
94 | + counter.addMultiLineComment("<!--","-->") | |
95 | + counter.setFileType("ASP") | |
96 | + self.counter_map["ASP"] = counter | |
97 | + return counter | |
98 | + | |
99 | + elif (fileName.endswith(".html") or fileName.endswith(".htm")): | |
100 | + counter = None | |
101 | + if self.counter_map.has_key("HTML"): | |
102 | + counter = self.counter_map["HTML"] | |
103 | + else: | |
104 | + counter = self.createXMLCounter("HTML") | |
105 | + self.counter_map["HTML"] = counter | |
106 | + return counter | |
107 | + | |
108 | + elif (fileName.endswith(".vbs")): | |
109 | + counter = None | |
110 | + if self.counter_map.has_key("vbs"): | |
111 | + counter = self.counter_map["vbs"] | |
112 | + else: | |
113 | + counter = self.createVBCounter("vbs") | |
114 | + self.counter_map["vbs"] = counter | |
115 | + return counter | |
116 | + | |
117 | + elif (fileName.endswith(".bas") or fileName.endswith(".frm") or fileName.endswith(".cls")): | |
118 | + counter = None | |
119 | + if self.counter_map.has_key("VB"): | |
120 | + counter = self.counter_map["VB"] | |
121 | + else: | |
122 | + counter = self.createVBCounter("VB") | |
123 | + self.counter_map["VB"] = counter | |
124 | + return counter | |
125 | + | |
126 | + elif (fileName.endswith(".vb")): | |
127 | + counter = None | |
128 | + if self.counter_map.has_key("VB.NET"): | |
129 | + counter = self.counter_map["VB.NET"] | |
130 | + else: | |
131 | + counter = self.createVBCounter("VB.NET") | |
132 | + self.counter_map["VB.NET"] = counter | |
133 | + return counter | |
134 | + | |
135 | + elif (fileName.endswith(".pl") or fileName.endswith(".pm")): | |
136 | + counter = None | |
137 | + if self.counter_map.has_key("Perl"): | |
138 | + counter = self.counter_map["Perl"] | |
139 | + else: | |
140 | + counter = DefaultStepCounter() | |
141 | + counter.addSingleLineComment("#") | |
142 | + counter.addMultiLineComment("=pod","=cut") | |
143 | + counter.setFileType("Perl") | |
144 | + self.counter_map["Perl"] = counter | |
145 | + return counter | |
146 | + | |
147 | + elif (fileName.endswith(".py")): | |
148 | + counter = None | |
149 | + if self.counter_map.has_key("Python"): | |
150 | + counter = self.counter_map["Python"] | |
151 | + else: | |
152 | + counter = DefaultStepCounter() | |
153 | + counter.addSingleLineComment("#") | |
154 | + counter.setFileType("Python") | |
155 | + self.counter_map["Python"] = counter | |
156 | + return counter | |
157 | + | |
158 | + elif (fileName.endswith(".rb")): | |
159 | + counter = None | |
160 | + if self.counter_map.has_key("Ruby"): | |
161 | + counter = self.counter_map["Ruby"] | |
162 | + else: | |
163 | + counter = DefaultStepCounter() | |
164 | + counter.addSingleLineComment("#") | |
165 | + counter.addMultiLineComment("=begin","=end") | |
166 | + counter.setFileType("Ruby") | |
167 | + self.counter_map["Ruby"] = counter | |
168 | + return counter | |
169 | + | |
170 | + elif (fileName.endswith(".tcl")): | |
171 | + return self.createShellCounter("Tcl"); | |
172 | + | |
173 | + elif (fileName.endswith(".sql")): | |
174 | + counter = None | |
175 | + if self.counter_map.has_key("SQL"): | |
176 | + counter = self.counter_map["SQL"] | |
177 | + else: | |
178 | + counter = DefaultStepCounter() | |
179 | + counter.addSingleLineComment("#") | |
180 | + counter.addSingleLineComment("--") | |
181 | + counter.addSingleLineComment("REM") | |
182 | + counter.addMultiLineComment("/*","*/"); | |
183 | + counter.setFileType("SQL") | |
184 | + self.counter_map["SQL"] = counter | |
185 | + return counter | |
186 | + | |
187 | + elif (fileName.endswith(".cfm")): | |
188 | + counter = None | |
189 | + if self.counter_map.has_key("CFM"): | |
190 | + counter = self.counter_map["CFM"] | |
191 | + else: | |
192 | + counter = DefaultStepCounter() | |
193 | + counter.addMultiLineComment("<!--","-->") | |
194 | + counter.addMultiLineComment("<!---","--->") | |
195 | + counter.setFileType("CFM") | |
196 | + self.counter_map["CFM"] = counter | |
197 | + return counter | |
198 | + | |
199 | + elif (fileName.endswith(".properties")): | |
200 | + counter = None | |
201 | + if self.counter_map.has_key("Properties"): | |
202 | + counter = self.counter_map["Properties"] | |
203 | + else: | |
204 | + counter = self.createShellCounter("Properties") | |
205 | + self.counter_map["Properties"] = counter | |
206 | + return counter | |
207 | + | |
208 | + elif (fileName.endswith(".xml")): | |
209 | + counter = None | |
210 | + if self.counter_map.has_key("XML"): | |
211 | + counter = self.counter_map["XML"] | |
212 | + else: | |
213 | + counter = self.createXMLCounter("XML") | |
214 | + self.counter_map["XML"] = counter | |
215 | + return counter | |
216 | + | |
217 | + elif (fileName.endswith(".xsl") or fileName.endswith(".xslt")): | |
218 | + counter = None | |
219 | + if self.counter_map.has_key("XSLT"): | |
220 | + counter = self.counter_map["XSLT"] | |
221 | + else: | |
222 | + counter = self.createXMLCounter("XSLT") | |
223 | + self.counter_map["XSLT"] = counter | |
224 | + return counter | |
225 | + | |
226 | + elif (fileName.endswith(".dtd")): | |
227 | + counter = None | |
228 | + if self.counter_map.has_key("DTD"): | |
229 | + counter = self.counter_map["DTD"] | |
230 | + else: | |
231 | + counter = self.createXMLCounter("DTD") | |
232 | + self.counter_map["DTD"] = counter | |
233 | + return counter | |
234 | + | |
235 | + elif (fileName.endswith(".tld")): | |
236 | + counter = None | |
237 | + if self.counter_map.has_key("TLD"): | |
238 | + counter = self.counter_map["TLD"] | |
239 | + else: | |
240 | + counter = self.createXMLCounter("TLD") | |
241 | + self.counter_map["TLD"] = counter | |
242 | + return counter | |
243 | + | |
244 | + elif (fileName.endswith(".xsd")): | |
245 | + counter = None | |
246 | + if self.counter_map.has_key("XMLSchema"): | |
247 | + counter = self.counter_map["XMLSchema"] | |
248 | + else: | |
249 | + counter = self.createXMLCounter("XMLSchema") | |
250 | + self.counter_map["XMLSchema"] = counter | |
251 | + return counter | |
252 | + | |
253 | + elif (fileName.endswith(".bat")): | |
254 | + counter = None | |
255 | + if self.counter_map.has_key("BAT"): | |
256 | + counter = self.counter_map["BAT"] | |
257 | + else: | |
258 | + counter = DefaultStepCounter() | |
259 | + counter.addSingleLineComment("REM") | |
260 | + counter.setFileType("BAT") | |
261 | + self.counter_map["BAT"] = counter | |
262 | + return counter | |
263 | + | |
264 | + elif (fileName.endswith(".css")): | |
265 | + counter = None | |
266 | + if self.counter_map.has_key("CSS"): | |
267 | + counter = self.counter_map["CSS"] | |
268 | + else: | |
269 | + counter = DefaultStepCounter() | |
270 | + counter.addMultiLineComment("/*","*/") | |
271 | + counter.setFileType("CSS") | |
272 | + self.counter_map["CSS"] = counter | |
273 | + return counter | |
274 | + | |
275 | + elif (fileName.endswith(".l") or fileName.endswith(".el") or fileName.endswith(".cl")): | |
276 | + counter = None | |
277 | + if self.counter_map.has_key("Lisp"): | |
278 | + counter = self.counter_map["Lisp"] | |
279 | + else: | |
280 | + counter = DefaultStepCounter() | |
281 | + counter.addSingleLineComment(";") | |
282 | + counter.setFileType("Lisp") | |
283 | + self.counter_map["Lisp"] = counter | |
284 | + return counter | |
285 | + | |
286 | + elif (fileName.endswith(".ini")): | |
287 | + counter = None | |
288 | + if self.counter_map.has_key("INI"): | |
289 | + counter = self.counter_map["INI"] | |
290 | + else: | |
291 | + counter = DefaultStepCounter() | |
292 | + counter.addSingleLineComment(";") | |
293 | + counter.setFileType("INI") | |
294 | + self.counter_map["INI"] = counter | |
295 | + return counter | |
296 | + | |
297 | + else: | |
298 | + return None | |
299 | + | |
300 | + def createJavaCounter(self,name): | |
301 | + counter = DefaultStepCounter() | |
302 | + counter.addSingleLineComment("//") | |
303 | + counter.addMultiLineComment("/*","*/") | |
304 | + counter.setFileType(name) | |
305 | + return counter | |
306 | + | |
307 | + def createVBCounter(self,name): | |
308 | + counter = DefaultStepCounter() | |
309 | + counter.addSingleLineComment("'") | |
310 | + counter.addSingleLineComment("REM") | |
311 | + counter.setFileType(name) | |
312 | + return counter | |
313 | + | |
314 | + def createShellCounter(self,name): | |
315 | + counter = DefaultStepCounter() | |
316 | + counter.addSingleLineComment("#") | |
317 | + counter.setFileType(name) | |
318 | + return counter | |
319 | + | |
320 | + def createXMLCounter(self,name): | |
321 | + counter = DefaultStepCounter() | |
322 | + counter.addMultiLineComment("<!--","-->") | |
323 | + counter.setFileType(name) | |
324 | + return counter | |
325 | + | |
326 | +class DefaultStepCounter: | |
327 | + | |
328 | + singleLineComment = [] | |
329 | + multiLineComment = [] | |
330 | + fileType = "UNDEF" | |
331 | + | |
332 | + def setFileType(self, fileType): | |
333 | + self.fileType = fileType | |
334 | + | |
335 | + def getFileType(self): | |
336 | + return self.fileType | |
337 | + | |
338 | + def addSingleLineComment(self, str): | |
339 | + self.singleLineComment.append(str) | |
340 | + | |
341 | + def getSingleLineComment(self): | |
342 | + return self.singleLineComment | |
343 | + | |
344 | + def addMultiLineComment(self, comment_from,comment_to): | |
345 | + self.multiLineComment.append([comment_from,comment_to]) | |
346 | + | |
347 | + def getMultiLineComment(self): | |
348 | + return self.multiLineComment | |
349 | + | |
350 | + def countContent(self,content): | |
351 | + step = 0 | |
352 | + non = 0 | |
353 | + comment = 0 | |
354 | + multiLineFlag = False | |
355 | + lastLineComment = [] | |
356 | + for line in content.split("\n"): | |
357 | + if(multiLineFlag==False): | |
358 | + if(self.nonCheck(line.strip())): | |
359 | + non = non + 1 | |
360 | + elif(self.isSingleLineComment(line.strip())): | |
361 | + comment = comment + 1 | |
362 | + else: | |
363 | + lastLineComment = self.isMultiLineCommentStart(line,comment) | |
364 | + if(lastLineComment!=None): | |
365 | + comment = comment + 1 | |
366 | + multiLineFlag = True | |
367 | + else: | |
368 | + step = step + 1 | |
369 | + else: | |
370 | + comment = comment + 1 | |
371 | + if (self.isMultiLineCommentEnd(line,lastLineComment)): | |
372 | + multiLineFlag = False | |
373 | + | |
374 | + return StepCountResult('',self.getFileType(),step,non,comment) | |
375 | + | |
376 | + def nonCheck(self,line): | |
377 | + if(line==''): | |
378 | + return True | |
379 | + else: | |
380 | + return False | |
381 | + | |
382 | + def isSingleLineComment(self, line): | |
383 | + dim = self.getSingleLineComment() | |
384 | + for d in dim: | |
385 | + if(line.startswith(d)): | |
386 | + return True | |
387 | + | |
388 | + area = self.getMultiLineComment() | |
389 | + for a in area: | |
390 | + start = a[0] | |
391 | + end = a[1] | |
392 | + | |
393 | + idx = line.find(start); | |
394 | + if(idx==0 and line.find(end,idx)==len(line)-len(end)): | |
395 | + return True | |
396 | + | |
397 | + return False | |
398 | + | |
399 | + def isMultiLineCommentStart(self,line,comment): | |
400 | + area = self.getMultiLineComment() | |
401 | + for a in area: | |
402 | + start = a[0] | |
403 | + end = a[1] | |
404 | + | |
405 | + idx = line.find(start) | |
406 | + if(idx>=0 and line.find(end,idx)<0): | |
407 | + return a | |
408 | + return None | |
409 | + | |
410 | + def isMultiLineCommentEnd(self,line,area): | |
411 | + end = area[1] | |
412 | + if(line.find(end)>=0): | |
413 | + return True | |
414 | + else: | |
415 | + return False | |
416 | + | |
417 | +class StepCountResult: | |
418 | + | |
419 | + def __init__(self,fileName,fileType,step,nonStep,comment): | |
420 | + self.fileName = fileName | |
421 | + self.fileType = fileType | |
422 | + self.step = step | |
423 | + self.nonStep = nonStep | |
424 | + self.commentStep = comment | |
425 | + | |
426 | + def getFileName(self): | |
427 | + return self.fileName | |
428 | + | |
429 | + def getFileType(self): | |
430 | + return self.fileType | |
431 | + | |
432 | + def getStep(self): | |
433 | + return self.step | |
434 | + | |
435 | + def getNonStep(self): | |
436 | + return self.nonStep | |
437 | + | |
438 | + def getCommentStep(self): | |
439 | + return self.commentStep | |
440 | + | |
441 | + def toString(self): | |
442 | + return "%s,%s,%d,%d,%d" % self.getFileName(),self.getFileType(),self.getStep(),self.getNonStep,self.getCommentStep() | |
443 | + |
@@ -0,0 +1,105 @@ | ||
1 | +<!DOCTYPE html | |
2 | + PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | |
3 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
4 | +<html xmlns="http://www.w3.org/1999/xhtml" | |
5 | + xmlns:py="http://genshi.edgewall.org/" | |
6 | + xmlns:xi="http://www.w3.org/2001/XInclude"> | |
7 | + <xi:include href="layout.html" /> | |
8 | + <xi:include href="macros.html" /> | |
9 | + <head> | |
10 | + <title>$path</title> | |
11 | + </head> | |
12 | +<?python | |
13 | +import os | |
14 | +?> | |
15 | + <body> | |
16 | + <div id="content" class="browser"> | |
17 | + <div id="jumprev"> | |
18 | + <form action="" method="get"> | |
19 | + <div> | |
20 | + <B>PATH:${path}</B> | |
21 | + </div> | |
22 | + <div> | |
23 | + <label for="rev" title="${stickyrev and _('Hint: clear the field to view latest revision') or None}"> | |
24 | + ${_('View revision')}:</label> | |
25 | + <input type="text" id="rev" name="rev" value="$stickyrev" size="6" /> | |
26 | + </div> | |
27 | + </form> | |
28 | + </div> | |
29 | + | |
30 | + <table class="listing" id="dirlist"> | |
31 | + <thead> | |
32 | + <tr> | |
33 | + <th class="rev">${_('Directory Name')}</th> | |
34 | + <th class="rev">${_('File Name')}</th> | |
35 | + <th class="rev">${_('Size')}</th> | |
36 | + <th class="rev">${_('Rev')}</th> | |
37 | + <th class="rev">${_('Age')}</th> | |
38 | + <th class="change">${_('Last Change')}</th> | |
39 | + <th class="change">ログ</th> | |
40 | + <th class="change">タイプ</th> | |
41 | + <th class="change">実ステップ行数</th> | |
42 | + <th class="change">コメント行数</th> | |
43 | + <th class="change">コメント率</th> | |
44 | + </tr> | |
45 | + </thead> | |
46 | + <tbody> | |
47 | + <tr class="odd" py:if="path != '/'"> | |
48 | + <td colspan='11'><a href="${href.stepcounter(path[0:path.rindex('/')], rev=stickyrev, order=(dir.order != 'name' and dir.order or None), desc=dir.desc)}">..</a></td> | |
49 | + </tr> | |
50 | + | |
51 | + <py:for each="idx, entry in enumerate(dir.entries)"> | |
52 | + <py:with vars="change = dir.changes.get(entry.rev)"> | |
53 | + <tr class="${idx % 2 and 'even' or 'odd'}"> | |
54 | + <td class="name"> | |
55 | + <a class="$entry.kind" title="${_('View %(kind)s', kind=(entry.kind == entry.DIRECTORY and _('Directory') or _('File')))}" | |
56 | + href="${href.stepcounter(os.path.dirname(entry.path), rev=stickyrev, order=(dir.order != 'name' and dir.order or None), desc=dir.desc)}">${os.path.dirname(entry.path)}</a> | |
57 | + </td> | |
58 | + <td class="name"> | |
59 | + <a class="$entry.kind" title="${_('View %(kind)s', kind=(entry.kind == entry.DIRECTORY and _('Directory') or _('File')))}" | |
60 | + href="${href.browser(entry.path, rev=stickyrev, order=(dir.order != 'name' and dir.order or None), desc=dir.desc)}">$entry.name</a> | |
61 | + </td> | |
62 | + <td class="size">${sizeinfo(entry.content_length)}</td> | |
63 | + <td class="rev"> | |
64 | + <a title="${_('View Revision Log')}" href="${href.log(entry.path, rev=rev)}">$entry.rev</a> | |
65 | + </td> | |
66 | + <td class="age" style="${None}"> | |
67 | + ${change and dateinfo(change.date) or '-'} | |
68 | + </td> | |
69 | + <td class="author"> | |
70 | + <span class="author" py:if="change"><a class="$entry.kind" href="${href.stepcounter(path, rev=stickyrev, order=(dir.order != 'name' and dir.order or None), desc=dir.desc,commiter=change.author)}">${authorinfo(change.author)}</a></span> | |
71 | + </td> | |
72 | + <td class="change"> | |
73 | + <span class="change" py:choose="" py:with="chgset_context = context('changeset', change.rev)"> | |
74 | + <py:when test="not change or 'CHANGESET_VIEW' not in perm(chgset_context.resource)">-</py:when> | |
75 | + <py:when test="wiki_format_messages"> | |
76 | + ${change and wiki_to_oneliner(chgset_context, change.message, shorten=True)} | |
77 | + </py:when> | |
78 | + <py:otherwise>${change and shorten_line(change.message)}</py:otherwise> | |
79 | + </span> | |
80 | + </td> | |
81 | + <td py:if="entry.isdir==False">${dir.counts[idx].fileType}</td> | |
82 | + <td py:if="entry.isdir==False" class="count">${dir.counts[idx].step}</td> | |
83 | + <td py:if="entry.isdir==False" class="count">${dir.counts[idx].commentStep}</td> | |
84 | + <td py:if="entry.isdir==False" class="count"> | |
85 | + <py:choose> | |
86 | + <py:when test="dir.counts[idx].step == 0">N/A</py:when> | |
87 | + <py:otherwise> | |
88 | + ${'%3.1f' % (float(dir.counts[idx].commentStep*100)/float(dir.counts[idx].commentStep+dir.counts[idx].step))}% | |
89 | + </py:otherwise> | |
90 | + </py:choose> | |
91 | + </td> | |
92 | + </tr> | |
93 | + </py:with> | |
94 | + </py:for> | |
95 | + <tr> | |
96 | + <td colspan='8'>合計</td> | |
97 | + <td class="count">${dir.sum_step}</td> | |
98 | + <td class="count">${dir.sum_comment}</td> | |
99 | + <td class="count">${'%3.1f' % (float(dir.sum_comment*100)/float(dir.sum_step+dir.sum_comment))}%</td> | |
100 | + </tr> | |
101 | + </tbody> | |
102 | + </table> | |
103 | + </div> | |
104 | + </body> | |
105 | +</html> |