source: analyzeplugin/0.12/analyze/web_ui.py

Last change on this file was 13992, checked in by Ryan J Ollos, 9 years ago

Changed to 3-Clause BSD license with permission of author. Refs #11832.

File size: 6.1 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2011-2014 Rob Guttman <guttman@alum.mit.edu>
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution.
8#
9
10import re
11import json
12from trac.core import *
13from trac.web.chrome import ITemplateProvider, add_script, add_stylesheet
14from trac.web.main import IRequestFilter, IRequestHandler
15from trac.perm import IPermissionRequestor
16from analysis import *
17
18class AnalyzeModule(Component):
19    """Base component for analyzing tickets."""
20
21    implements(IRequestHandler, ITemplateProvider, IRequestFilter,
22               IPermissionRequestor)
23
24    analyses = ExtensionPoint(IAnalysis)
25
26    # IPermissionRequestor methods
27    def get_permission_actions(self):
28        return ['ANALYZE_VIEW']
29
30    # ITemplateProvider methods
31    def get_htdocs_dirs(self):
32        from pkg_resources import resource_filename
33        return [('analyze', resource_filename(__name__, 'htdocs'))]
34
35    def get_templates_dirs(self):
36        from pkg_resources import resource_filename
37        return [resource_filename(__name__, 'templates')]
38
39    # IRequestFilter methods
40    def pre_process_request(self, req, handler):
41        return handler
42
43    def post_process_request(self, req, template, data, content_type):
44        if self._valid_request(req):
45            add_stylesheet(req, 'analyze/analyze.css')
46            add_stylesheet(req, 'analyze/jquery-ui-1.8.16.custom.css')
47            add_script(req, 'analyze/jquery-ui-1.8.16.custom.min.js')
48            add_script(req, '/analyze/analyze.html')
49            add_script(req, 'analyze/analyze.js')
50        return template, data, content_type
51
52    # IRequestHandler methods
53    def match_request(self, req):
54        return req.path_info.startswith('/analyze/')
55
56    def process_request(self, req):
57        data = {'analyses':self._get_analyses(req, check_referer=True),
58                'report':get_report(req, check_referer=True)}
59        return 'analyze.html', data, 'text/javascript'
60
61    # private methods
62    def _valid_request(self, req):
63        """Checks permissions and that report can be analyzed."""
64        if req.perm.has_permission('ANALYZE_VIEW') and \
65          'action=' not in req.query_string and \
66          self._get_analyses(req):
67            return True
68        return False
69
70    def _get_analyses(self, req, check_referer=False):
71        """Returns a list of analyses for the given report."""
72        report = get_report(req, check_referer)
73        analyses = []
74        for analysis in self.analyses:
75            if report and analysis.can_analyze(report):
76                analyses.append(analysis)
77        return analyses
78
79
80class AnalyzeAjaxModule(Component):
81    """Ajax handler for suggesting solutions to users and fixing issues."""
82    implements(IRequestHandler)
83
84    analyses = ExtensionPoint(IAnalysis)
85
86    # IRequestHandler methods
87    def match_request(self, req):
88        if req.path_info.startswith('/analyzeajax/'):
89            return True
90
91    def process_request(self, req):
92        """Process AJAX request."""
93        try:
94            if req.path_info.endswith('/list'):
95                result = self._get_analyses(req.args['report'])
96            else:
97                for analysis in self.analyses:
98                    if not req.path_info.endswith('/'+analysis.path) and  \
99                       not req.path_info.endswith('/'+analysis.path+'/fix'):
100                        continue
101                    db = self.env.get_db_cnx()
102                    if req.path_info.endswith('/fix'):
103                        result = self._fix_issue(analysis, db, req)
104                    else:
105                        report = get_report(req, check_referer=True)
106                        result = self._get_solutions(analysis, db, req, report)
107                    break
108                else:
109                    raise Exception("Unknown path: %s" % req.path_info)
110            code,type,msg = 200,'application/json',json.dumps(result)
111        except Exception, e:
112            import traceback;
113            code,type = 500,'text/plain'
114            msg = "Oops...\n"+traceback.format_exc()+"\n"
115        req.send_response(code)
116        req.send_header('Content-Type', type)
117        req.send_header('Content-Length', len(msg))
118        req.end_headers()
119        req.write(msg)
120
121    def _get_analyses(self, report):
122        result = []
123        for analysis in self.analyses:
124            if analysis.can_analyze(report):
125                result.append({
126                    'path': analysis.path,
127                    'num': analysis.num,
128                    'title': analysis.title,
129                })
130        return result
131
132    def _get_solutions(self, analysis, db, req, report):
133        """Return the solutions with serialized data to use for the fix.
134        Ticket references are converted to hrefs."""
135        issue,solutions = analysis.get_solutions(db, req.args, report)
136        base = req.base_url+'/ticket/'
137        id = re.compile(r"(#([1-9][0-9]*))")
138        issue = id.sub(r'<a href="%s\2">\1</a>' % base, issue)
139
140        # serialize solution data; convert ticket refs to hrefs
141        for solution in solutions:
142            solution['disabled'] = not req.perm.has_permission('TICKET_MODIFY')
143            solution['data'] = json.dumps(solution['data'])
144            name = solution['name']
145            solution['name'] = id.sub(r'<a href="%s\2">\1</a>' % base, name)
146
147        return {'title': analysis.title, 'label': issue,
148                'exists': len(solutions) > 0, 'solutions': solutions,
149                'refresh': analysis.get_refresh_report() or \
150                           get_report(req, check_referer=True)}
151
152    def _fix_issue(self, analysis, db, req):
153        """Return the result of the fix."""
154        data = json.loads(req.args['data'])
155        return analysis.fix_issue(db, data, req.authname)
156
157
158# common functions
159def get_report(req, check_referer=False):
160    """Returns the report number as a string."""
161    report_re = re.compile(r"/report/(?P<num>[1-9][0-9]*)")
162    if check_referer:
163        path = req.environ.get('HTTP_REFERER','')
164    else:
165        path = req.path_info
166    match = report_re.search(path)
167    if match:
168        return match.groupdict()['num']
169    return None
Note: See TracBrowser for help on using the repository browser.