~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/controllers/__init__.py

  • Committer: Michael Hudson
  • Date: 2007-12-12 08:36:51 UTC
  • mto: This revision was merged to the branch mainline in revision 143.
  • Revision ID: michael.hudson@canonical.com-20071212083651-oyh34swjxbzceyi1
add a comment for spiv

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#
2
 
# Copyright (C) 2008  Canonical Ltd.
3
2
# Copyright (C) 2006  Robey Pointer <robey@lag.net>
4
3
# Copyright (C) 2006  Goffredo Baroncelli <kreijack@inwind.it>
5
4
#
16
15
# You should have received a copy of the GNU General Public License
17
16
# along with this program; if not, write to the Free Software
18
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
#
19
19
 
20
 
import bzrlib.errors
 
20
import logging
 
21
import os
 
22
import re
 
23
import sys
21
24
import time
22
25
 
23
 
from paste.httpexceptions import HTTPNotFound
24
 
from paste.request import path_info_pop, parse_querystring
 
26
import turbogears
 
27
from turbogears import controllers
 
28
from cherrypy import NotFound
 
29
from configobj import ConfigObj
25
30
 
26
31
from loggerhead import util
27
 
from loggerhead.templatefunctions import templatefunctions
28
 
from loggerhead.zptsupport import load_template
29
 
 
30
 
 
31
 
class BufferingWriter(object):
32
 
 
33
 
    def __init__(self, writefunc, buf_limit):
34
 
        self.bytes = 0
35
 
        self.buf = []
36
 
        self.buflen = 0
37
 
        self.writefunc = writefunc
38
 
        self.buf_limit = buf_limit
39
 
 
40
 
    def flush(self):
41
 
        self.writefunc(''.join(self.buf))
42
 
        self.buf = []
43
 
        self.buflen = 0
44
 
 
45
 
    def write(self, data):
46
 
        self.buf.append(data)
47
 
        self.buflen += len(data)
48
 
        self.bytes += len(data)
49
 
        if self.buflen > self.buf_limit:
50
 
            self.flush()
51
 
 
52
 
 
53
 
class TemplatedBranchView(object):
54
 
 
55
 
    template_path = None
56
 
 
57
 
    def __init__(self, branch, history_callable):
58
 
        self._branch = branch
59
 
        self._history_callable = history_callable
60
 
        self.__history = None
61
 
        self.log = branch.log
62
 
 
63
 
    @property
64
 
    def _history(self):
65
 
        if self.__history is not None:
66
 
            return self.__history
67
 
        self.__history = self._history_callable()
68
 
        return self.__history
69
 
 
70
 
    def __call__(self, environ, start_response):
71
 
        z = time.time()
72
 
        kwargs = dict(parse_querystring(environ))
73
 
        util.set_context(kwargs)
74
 
        args = []
75
 
        while True:
76
 
            arg = path_info_pop(environ)
77
 
            if arg is None:
78
 
                break
79
 
            args.append(arg)
80
 
 
81
 
        path = None
82
 
        if len(args) > 1:
83
 
            path = unicode('/'.join(args[1:]), 'utf-8')
84
 
        self.args = args
85
 
 
86
 
        vals = {
87
 
            'static_url': self._branch.static_url,
88
 
            'branch': self._branch,
 
32
from loggerhead.branchview import BranchView
 
33
from loggerhead.history import History, is_branch
 
34
 
 
35
log = logging.getLogger("loggerhead.controllers")
 
36
 
 
37
 
 
38
def cherrypy_friendly(s):
 
39
    """
 
40
    convert a config section name into a name that pleases cherrypy.
 
41
    """
 
42
    return re.sub(r'[^\w\d_]', '_', s)
 
43
 
 
44
 
 
45
class Project (object):
 
46
    def __init__(self, name, config):
 
47
        self.name = name
 
48
        self.friendly_name = config.get('name', name)
 
49
        self.description = config.get('description', '')
 
50
        self.long_description = config.get('long_description', '')
 
51
        self._config = config
 
52
        
 
53
        self._views = []
 
54
        for view_name in config.sections:
 
55
            log.debug('Configuring (project %s) branch %s...', name, view_name)
 
56
            self._add_view(view_name, config[view_name], config[view_name].get('folder'))
 
57
        
 
58
        self._auto_folder = config.get('auto_publish_folder', None)
 
59
        self._auto_list = []
 
60
        if self._auto_folder is not None:
 
61
            self._recheck_auto_folders()
 
62
    
 
63
    def _recheck_auto_folders(self):
 
64
        if self._auto_folder is None:
 
65
            return
 
66
        auto_list = []
 
67
        # scan a folder for bazaar branches, and add them automatically
 
68
        for path, folders, filenames in os.walk(self._auto_folder):
 
69
            for folder in folders:
 
70
                folder = os.path.join(path, folder)
 
71
                if is_branch(folder):
 
72
                    auto_list.append(folder)
 
73
        auto_list.sort()
 
74
        if auto_list == self._auto_list:
 
75
            # nothing has changed; do nothing.
 
76
            return
 
77
 
 
78
        # rebuild views:
 
79
        log.debug('Rescanning auto-folder for project %s ...', self.name)
 
80
        self._views = []
 
81
        for folder in auto_list:
 
82
            view_name = os.path.basename(folder)
 
83
            log.debug('Auto-configuring (project %s) branch %s...', self.name, view_name)
 
84
            self._add_view(view_name, ConfigObj(), folder)
 
85
        self._auto_list = auto_list
 
86
        
 
87
    def _add_view(self, view_name, view_config, folder):
 
88
        c_view_name = cherrypy_friendly(view_name)
 
89
        view = BranchView(self.name, c_view_name, view_name, folder, view_config, self._config)
 
90
        self._views.append(view)
 
91
        setattr(self, c_view_name, view)
 
92
        
 
93
    views = property(lambda self: self._views)
 
94
 
 
95
 
 
96
class Root (controllers.RootController):
 
97
    def __init__(self, config):
 
98
        self._projects = []
 
99
        self._config = config
 
100
        for project_name in self._config.sections:
 
101
            c_project_name = cherrypy_friendly(project_name)
 
102
            project = Project(c_project_name, self._config[project_name])
 
103
            self._projects.append(project)
 
104
            setattr(self, c_project_name, project)
 
105
        
 
106
    @turbogears.expose(template='loggerhead.templates.browse')
 
107
    def index(self):
 
108
        for p in self._projects:
 
109
            p._recheck_auto_folders()
 
110
        return {
 
111
            'projects': self._projects,
89
112
            'util': util,
90
 
            'url': self._branch.context_url,
 
113
            'title': self._config.get('title', ''),
91
114
        }
92
 
        vals.update(templatefunctions)
93
 
        headers = {}
94
 
 
95
 
        vals.update(self.get_values(path, kwargs, headers))
96
 
 
97
 
        self.log.info('Getting information for %s: %.3f secs' % (
98
 
            self.__class__.__name__, time.time() - z))
99
 
        if 'Content-Type' not in headers:
100
 
            headers['Content-Type'] = 'text/html'
101
 
        writer = start_response("200 OK", headers.items())
102
 
        if environ.get('REQUEST_METHOD') == 'HEAD':
103
 
            # No content for a HEAD request
104
 
            return []
105
 
        template = load_template(self.template_path)
106
 
        z = time.time()
107
 
        w = BufferingWriter(writer, 8192)
108
 
        template.expand_into(w, **vals)
109
 
        w.flush()
110
 
        self.log.info(
111
 
            'Rendering %s: %.3f secs, %s bytes' % (
112
 
                self.__class__.__name__, time.time() - z, w.bytes))
113
 
        return []
114
 
 
115
 
    def get_revid(self):
116
 
        h = self._history
117
 
        if h is None:
118
 
            return None
119
 
        if len(self.args) > 0 and self.args != ['']:
120
 
            try:
121
 
                revid = h.fix_revid(self.args[0])
122
 
            except bzrlib.errors.NoSuchRevision:
123
 
                raise HTTPNotFound;
124
 
        else:
125
 
            revid = h.last_revid
126
 
        return revid
 
115
 
 
116
    def _check_rebuild(self):
 
117
        for p in self._projects:
 
118
            for v in p.views:
 
119
                v.check_rebuild()
 
120
 
 
121
 
 
122
 
 
123
# for use in profiling the very-slow get_change() method:
 
124
#h = Root.bazaar.bzr_dev.get_history()
 
125
#w = list(h.get_revision_history())
 
126
#h._get_changes_profiled(w[:100])
 
127