1 from datetime import datetime
2 from urllib import urlencode
4 from jinja2 import Environment, FileSystemLoader
7 from libbe import storage
8 from libbe import bugdir
9 from libbe.command.depend import get_blocked_by, get_blocks
10 from libbe.command.target import bug_from_target_summary, bug_target
11 from libbe.command.util import bug_comment_from_user_id
12 from libbe.storage.util import settings_object
15 EMPTY = settings_object.EMPTY
17 def datetimeformat(value, format='%B %d, %Y at %I:%M %p'):
18 """Takes a timestamp and revormats it into a human-readable string."""
19 return datetime.fromtimestamp(value).strftime(format)
23 """The web interface to CFBE."""
25 def __init__(self, bug_root, template_root):
26 """Initialize the bug repository for this web interface."""
27 self.bug_root = bug_root
28 store = storage.get_storage(self.bug_root)
30 version = store.storage_version()
32 self.bd = bugdir.BugDir(store, from_storage=True)
33 self.repository_name = self.bug_root.split('/')[-1]
34 self.env = Environment(loader=FileSystemLoader(template_root))
35 self.env.filters['datetimeformat'] = datetimeformat
37 def get_common_information(self):
38 """Returns a dict of common information that most pages will need."""
39 possible_assignees = list(set(
40 [unicode(bug.assigned) for bug in self.bd if bug.assigned != EMPTY]))
41 possible_assignees.sort(key=unicode.lower)
43 possible_targets = list(set(
44 [unicode(bug.summary.rstrip("\n")) for bug in self.bd \
45 if bug.severity == u"target"]))
47 possible_targets.sort(key=unicode.lower)
49 possible_statuses = [u'open', u'assigned', u'test', u'unconfirmed',
50 u'closed', u'disabled', u'fixed', u'wontfix']
52 possible_severities = [u'minor', u'serious', u'critical', u'fatal',
55 return {'possible_assignees': possible_assignees,
56 'possible_targets': possible_targets,
57 'possible_statuses': possible_statuses,
58 'possible_severities': possible_severities,
59 'repository_name': self.repository_name,}
61 def filter_bugs(self, status, assignee, target):
62 """Filter the list of bugs to return only those desired."""
63 bugs = [bug for bug in self.bd if bug.status in status]
66 assignee = EMPTY if assignee == 'None' else assignee
67 bugs = [bug for bug in bugs if bug.assigned == assignee]
70 target = None if target == 'None' else target
72 # Return all bugs that don't block any targets.
73 return [bug for bug in bugs if not bug_target(self.bd, bug)]
75 # Return all bugs that block the supplied target.
76 targetbug = bug_from_target_summary(self.bd, target)
79 bugs = [bug for bug in get_blocked_by(self.bd, targetbug) if
85 def index(self, status='open', assignee='', target=''):
87 Bugs can be filtered by assignee or target.
88 The bug database will be reloaded on each visit."""
90 self.bd.load_all_bugs()
93 status = ['open', 'assigned', 'test', 'unconfirmed', 'wishlist']
94 label = 'All Open Bugs'
95 elif status == 'closed':
96 status = ['closed', 'disabled', 'fixed', 'wontfix']
97 label = 'All Closed Bugs'
100 label += ' Currently Unassigned' if assignee == 'None' \
101 else ' Assigned to %s' % (assignee,)
103 label += ' Currently Unscheduled' if target == 'None' \
104 else ' Scheduled for %s' % (target,)
106 bugs = self.filter_bugs(status, assignee, target)
108 template = self.env.get_template('empty-list.html')
110 template = self.env.get_template('list.html')
112 common_info = self.get_common_information()
113 return template.render(bugs=bugs, bd=self.bd, label=label,
114 assignees=common_info['possible_assignees'],
115 targets=common_info['possible_targets'],
116 statuses=common_info['possible_statuses'],
117 severities=common_info['possible_severities'],
118 repository_name=common_info['repository_name'],
123 def bug(self, id=''):
124 """The page for viewing a single bug."""
126 self.bd.load_all_bugs()
128 bug, comment = bug_comment_from_user_id(self.bd, id)
130 template = self.env.get_template('bug.html')
131 common_info = self.get_common_information()
133 # Determine which targets a bug has.
134 # First, is this bug blocking any other bugs?
136 blocks = get_blocks(self.bd, bug)
137 for targetbug in blocks:
138 # Are any of those blocked bugs targets?
139 blocker = self.bd.bug_from_uuid(targetbug.uuid)
140 if blocker.severity == "target":
141 targets += "%s " % blocker.summary
143 return template.render(bug=bug, bd=self.bd,
144 assignee='' if bug.assigned == EMPTY else bug.assigned,
146 assignees=common_info['possible_assignees'],
147 targets=common_info['possible_targets'],
148 statuses=common_info['possible_statuses'],
149 severities=common_info['possible_severities'],
150 repository_name=common_info['repository_name'])
154 def create(self, summary):
155 """The view that handles the creation of a new bug."""
156 if summary.strip() != '':
157 self.bd.new_bug(summary=summary).save()
158 raise cherrypy.HTTPRedirect('/', status=302)
162 def comment(self, id, body):
163 """The view that handles adding a comment."""
164 bug = self.bd.bug_from_uuid(id)
166 if body.strip() != '':
167 bug.comment_root.new_reply(body=body)
170 raise cherrypy.HTTPRedirect(
171 '/bug?%s' % urlencode({'id':bug.id.long_user()}),
175 def edit(self, id, status=None, target=None, assignee=None, severity=None, summary=None):
176 """The view that handles editing bug details."""
177 bug = self.bd.bug_from_uuid(id)
180 bug.summary = summary
182 bug.status = status if status != 'None' else None
183 bug.target = target if target != 'None' else None
184 bug.assigned = assignee if assignee != 'None' else None
185 bug.severity = severity if severity != 'None' else None
189 raise cherrypy.HTTPRedirect(
190 '/bug?%s' % urlencode({'id':bug.id.long_user()}),