"""
open/dulcinea/lib/ui/issues.qpy
"""
from dulcinea.common import format_user
from dulcinea.issue import Issue, get_issue_db
from dulcinea.user import text_format_user
from dulcinea.ui.lib.search import SearchForm
from dulcinea.ui.user.util import ensure_admin_access
from dulcinea.ui.util import none_quote, format_yes_or_no
from qp.fill.directory import Directory
from qp.fill.form import Form
from qp.fill.html import href
from qp.fill.widget import CheckboxWidget, SubmitWidget
from qp.fill.widget import StringWidget, TextWidget, RadiobuttonsWidget
from qp.mail.send import sendmail
from qp.pub.common import get_path, redirect, get_user, header, footer
from qp.pub.common import complete_path
class IssueDirectory(Directory):
def get_exports(self):
ensure_admin_access()
yield ('', '_q_index', 'Issues', 'Current software issues')
yield ('new', 'new', 'New', 'Create a new issue')
def _q_index(self):
return IssueSearchForm(get_issue_db().get_issues()).handle()
def new(self):
return issue_form(Issue())
def _q_lookup(self, component):
issue = get_issue_db().get_issue(component)
if issue:
return issue_form(issue)
else:
return None
def issue_link(issue):
return href(complete_path('/issues/%s' % issue.get_key()),
issue.get_title())
def format_priority (issue):
priority = issue.get_priority()
if priority:
return '%.2f' % priority
def format_status:xml(issue):
approvers = issue.get_approvers()
if approvers:
'Approved by %d of %d' % (len(approvers), len(issue.get_voters()))
users = get_issue_db().get_users_on_issue(issue)
if users:
'
'
', '.join([format_user(user, name=False, email=False)
for user in users])
'
'
class IssueSearchForm(SearchForm):
def get_search_text_fields(self, issue):
return [issue.get_key(),
str(issue.get_timestamp()),
issue.get_title(),
(issue.get_description() or ''),
' '.join([text_format_user(user)
for user in issue.get_voters()])]
def get_title(self):
return 'Issues'
def format_search_results_headings:xml(self):
'''
Title |
Business Value |
Cost |
Priority |
Status |
'''
def format_search_results_table_row:xml(self, issue):
'%s | ' % issue_link(issue)
'%s | ' % issue.get_bang_description(
issue.get_combined_bang())
'%s | ' % issue.get_buck_description(
issue.get_combined_buck())
'%s | ' % none_quote(format_priority(issue))
'%s | ' % none_quote(format_status(issue))
def footer:xml(self, title):
href('new', 'Report a new issue.')
SearchForm.footer(self, title)
def issue_form(issue):
form = Form()
user = get_user()
form.add(StringWidget, 'title',
value=issue.get_title(),
title='Title',
size=70,
required=1)
form.add(TextWidget, 'description',
value=issue.get_description(),
title='Description and Comments',
rows=20, cols=80)
form.add(RadiobuttonsWidget, 'bang',
title='Business Value',
value=issue.get_individual_bang(user),
options=zip(issue.BANG_OPTIONS,
issue.BANG_DESCRIPTIONS))
form.add(RadiobuttonsWidget, 'buck',
title='Cost Estimate',
value=issue.get_individual_buck(user),
options=zip(issue.BUCK_OPTIONS,
issue.BUCK_DESCRIPTIONS))
form.add(CheckboxWidget,'approval',
title='Resolved?',
value=issue.is_approved_by(user))
if issue.get_key() is None:
form.add(SubmitWidget, 'save', 'Save')
else:
form.add(SubmitWidget, 'save', 'Update')
form.add(SubmitWidget, 'in_progress', 'I\'m on it!')
form.add(SubmitWidget, 'cancel', 'Return to issues')
if form.get('cancel'):
redirect('.')
issue_db = get_issue_db()
if not form.is_submitted() or form.has_errors():
def render:xml():
if issue.get_key():
title = 'Issue %s' % issue.get_key()
else:
title = 'New issue'
header(title)
form.render()
voters = issue.get_voters()
if voters:
''
'
'
''
'User | '
'Business Value | '
'Cost | '
'Approved Resolution | '
'They\'re on it! | '
'
'
for user in voters:
''
'%s | ' % format_user(user, name=False, email=False)
'%s | ' % issue.get_bang_description(
issue.get_individual_bang(user))
'%s | ' % issue.get_buck_description(
issue.get_individual_buck(user))
'%s | ' % format_yes_or_no(
issue.is_approved_by(user))
'%s | ' % format_yes_or_no(
issue_db.get_issue_in_progress(user) is issue)
'
'
''
'Combined: | '
'%s | ' % issue.get_bang_description(
issue.get_combined_bang())
'%s | ' % issue.get_buck_description(
issue.get_combined_buck())
'Priority: | %s | ' % none_quote(
format_priority(issue))
'
'
'
'
'
'
footer(title=title)
return render()
issue.set_title(form['title'])
issue.set_description(form['description'])
issue.set_bang(user, form['bang'])
issue.set_buck(user, form['buck'])
issue.set_approval(user, form['approval'])
if issue.is_approved_by(user):
issue_db.clear_issue_in_progress(user, issue)
if form.get('in_progress'):
issue_db.set_issue_in_progress(user, issue)
if issue.get_key() is None:
issue_db.add_issue(issue)
url = './' + issue.get_key()
else:
url = get_path()
if issue.is_resolved():
notify_issue_resolved(issue)
issue_db.remove_issue(issue)
url = '.'
redirect(url)
def notify_issue_resolved(issue):
"""
Notify the involved parties that an issue has been resolved
"""
to_addrs = [user.get_email() for user in issue.get_approvers()
if user.get_email()]
subject = "RESOLVED: %s" % issue.get_title()
body = ""
body += 'title: %s\n' % issue.get_title()
body += 'timestamp: %s\n' % issue.get_timestamp()
body += 'key: %s\n' % issue.get_key()
body += 'description:\n%s' % issue.get_description()
sendmail(subject, body, to_addrs)