cfbe: typo: Unschdeuled -> Unscheduled
[be.git] / interfaces / web / web.py
1 from datetime import datetime
2 from urllib import urlencode
3
4 from jinja2 import Environment, FileSystemLoader
5 import cherrypy
6
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
13
14
15 EMPTY = settings_object.EMPTY
16
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)
20
21
22 class WebInterface:
23     """The web interface to CFBE."""
24     
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)
29         store.connect()
30         version = store.storage_version()
31         print 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
36     
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)
42         
43         possible_targets = list(set(
44           [unicode(bug.summary.rstrip("\n")) for bug in self.bd \
45                 if bug.severity == u"target"]))
46
47         possible_targets.sort(key=unicode.lower)
48         
49         possible_statuses = [u'open', u'assigned', u'test', u'unconfirmed', 
50                              u'closed', u'disabled', u'fixed', u'wontfix']
51         
52         possible_severities = [u'minor', u'serious', u'critical', u'fatal', 
53                                u'wishlist']
54         
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,}
60     
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]
64         
65         if assignee != '':
66             assignee = EMPTY if assignee == 'None' else assignee
67             bugs = [bug for bug in bugs if bug.assigned == assignee]
68         
69         if target != '':
70             target = None if target == 'None' else target
71             if target == None:
72                 # Return all bugs that don't block any targets.
73                 return [bug for bug in bugs if not bug_target(self.bd, bug)]
74             else:
75                 # Return all bugs that block the supplied target.
76                 targetbug = bug_from_target_summary(self.bd, target)
77                 if targetbug == None:
78                     return []
79                 bugs = [bug for bug in get_blocked_by(self.bd, targetbug) if
80                         bug.active]
81         return bugs
82     
83     
84     @cherrypy.expose
85     def index(self, status='open', assignee='', target=''):
86         """The main bug page.
87         Bugs can be filtered by assignee or target.
88         The bug database will be reloaded on each visit."""
89         
90         self.bd.load_all_bugs()
91         
92         if status == 'open':
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'
98         
99         if assignee != '':
100             label += ' Currently Unassigned' if assignee == 'None' \
101                 else ' Assigned to %s' % (assignee,)
102         if target != '':
103             label += ' Currently Unscheduled' if target == 'None' \
104                 else ' Scheduled for %s' % (target,)
105         
106         bugs = self.filter_bugs(status, assignee, target)
107         if len(bugs) == 0:
108             template = self.env.get_template('empty-list.html')
109         else:
110             template = self.env.get_template('list.html')
111
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'],
119                                urlencode=urlencode)
120     
121     
122     @cherrypy.expose
123     def bug(self, id=''):
124         """The page for viewing a single bug."""
125         
126         self.bd.load_all_bugs()
127         
128         bug, comment = bug_comment_from_user_id(self.bd, id)
129         
130         template = self.env.get_template('bug.html')
131         common_info = self.get_common_information()
132
133         # Determine which targets a bug has.
134         # First, is this bug blocking any other bugs?
135         targets = ''
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
142         
143         return template.render(bug=bug, bd=self.bd, 
144                                assignee='' if bug.assigned == EMPTY else bug.assigned,
145                                target=targets,
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'])
151     
152     
153     @cherrypy.expose
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)
159     
160     
161     @cherrypy.expose
162     def comment(self, id, body):
163         """The view that handles adding a comment."""
164         bug = self.bd.bug_from_uuid(id)
165         
166         if body.strip() != '':
167             bug.comment_root.new_reply(body=body)
168             bug.save()
169
170         raise cherrypy.HTTPRedirect(
171             '/bug?%s' % urlencode({'id':bug.id.long_user()}),
172             status=302)
173
174     @cherrypy.expose
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)
178         
179         if summary != None:
180             bug.summary = summary
181         else:
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
186             
187         bug.save()
188
189         raise cherrypy.HTTPRedirect(
190             '/bug?%s' % urlencode({'id':bug.id.long_user()}),
191             status=302)
192