from django.conf import settings
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.utils.html import conditional_escape
from django.utils.translation import ugettext_lazy as _
from djblets.datagrid.grids import Column, DateTimeColumn, \
DateTimeSinceColumn, DataGrid
from djblets.util.templatetags.djblets_utils import ageid
from reviewboard.reviews.models import Group, ReviewRequest, ReviewRequestDraft
from reviewboard.reviews.templatetags.reviewtags import render_star
class StarColumn(Column):
"""
A column used to indicate whether the object is "starred" or watched.
The star is interactive, allowing the user to star or unstar the object.
"""
def __init__(self, *args, **kwargs):
Column.__init__(self, *args, **kwargs)
self.image_url = settings.MEDIA_URL + "rb/images/star_on.png"
self.image_width = 16
self.image_height = 15
self.image_alt = "Starred"
self.detailed_label = "Starred"
self.shrink = True
def render_data(self, obj):
return render_star(self.datagrid.request.user, obj)
class ShipItColumn(Column):
"""
A column used to indicate whether someone has marked this review request
as "Ship It!"
"""
def __init__(self, *args, **kwargs):
Column.__init__(self, *args, **kwargs)
self.image_url = settings.MEDIA_URL + "rb/images/shipit.png"
self.image_width = 16
self.image_height = 16
self.image_alt = "Ship It!"
self.detailed_label = "Ship It!"
self.db_field = "shipit_count"
self.sortable = True
self.shrink = True
def render_data(self, review_request):
if review_request.shipit_count > 0:
return '' \
'
%s' \
'' % \
(settings.MEDIA_URL, settings.MEDIA_SERIAL,
self.image_alt, review_request.shipit_count)
return ""
class MyCommentsColumn(Column):
"""
A column meant to represent the status of the logged-in user's
comments on the review.
"""
def __init__(self, *args, **kwargs):
Column.__init__(self, *args, **kwargs)
self.image_url = settings.MEDIA_URL + "rb/images/comment-draft-small.png"
self.image_width = 16
self.image_height = 16
self.image_alt = _("My Comments")
self.detailed_label = _("My Comments")
self.shrink = True
# XXX It'd be nice to be able to sort on this, but datagrids currently
# can only sort based on stored (in the DB) values, not computed values.
def render_data(self, review_request):
user = self.datagrid.request.user
if user.is_anonymous():
return ""
reviews = review_request.reviews.filter(user=user)
if len(reviews) == 0:
return ""
image_url = None
image_alt = None
found_ship_it = False
# Priority is ranked in the following order:
#
# 1) Non-public (draft) reviews
# 2) Public reviews marked "Ship It"
# 3) Public reviews not marked "Ship It"
for review in reviews:
if not review.public:
image_url = self.image_url
image_alt = _("Comments drafted")
break
if review.ship_it:
found_ship_it = True
if not image_url:
if found_ship_it:
image_url = settings.MEDIA_URL + \
"rb/images/comment-shipit-small.png"
image_alt = _("Comments published. Ship it!")
else:
image_url = settings.MEDIA_URL + "rb/images/comment-small.png"
image_alt = _("Comments published")
return '
' % \
(image_url, settings.MEDIA_SERIAL, self.image_width,
self.image_height, image_alt)
class NewUpdatesColumn(Column):
"""
A column used to indicate whether the review request has any new updates
since the user last saw it.
"""
def __init__(self, *args, **kwargs):
Column.__init__(self, *args, **kwargs)
self.image_url = settings.MEDIA_URL + "rb/images/convo.png"
self.image_width = 18
self.image_height = 16
self.image_alt = "New Updates"
self.detailed_label = "New Updates"
self.shrink = True
def render_data(self, review_request):
user = self.datagrid.request.user
if review_request.new_review_count > 0:
return '
' % \
(self.image_url, self.image_width, self.image_height,
self.image_alt)
return ""
class SummaryColumn(Column):
"""
A column used to display a summary of the review request, along with
labels indicating if it's a draft or if it's submitted.
"""
def __init__(self, label=_("Summary"), *args, **kwargs):
Column.__init__(self, label=label, *args, **kwargs)
self.sortable = True
def render_data(self, review_request):
summary = conditional_escape(review_request.summary)
if not summary:
summary = ' No Summary'
if review_request.submitter == self.datagrid.request.user:
try:
draft = review_request.draft.get()
summary = conditional_escape(draft.summary)
return "[Draft] " + \
summary
except ReviewRequestDraft.DoesNotExist:
pass
if not review_request.public:
# XXX Do we want to say "Draft?"
return "[Draft] " + \
summary
if review_request.status == 'S':
return "[Submitted] " + \
summary
return summary
class PendingCountColumn(Column):
"""
A column used to show the pending number of review requests for a
group or user.
"""
def __init__(self, *args, **kwargs):
Column.__init__(self, *args, **kwargs)
def render_data(self, obj):
return str(getattr(obj, self.field_name).filter(public=True,
status='P').count())
class GroupMemberCountColumn(Column):
"""
A column used to show the number of users that registered for a group.
"""
def __init__(self, *args, **kwargs):
Column.__init__(self, *args, **kwargs)
self.link = True
self.link_func = self.link_to_object
def render_data(self, group):
return str(group.users.count())
def link_to_object(self, group, value):
return reverse('group_members', args=[group.name])
class ReviewCountColumn(Column):
"""
A column showing the number of reviews for a review request.
"""
def __init__(self, label=_("Reviews"),
detailed_label=_("Number of Reviews"),
*args, **kwargs):
Column.__init__(self, label=label, detailed_label=detailed_label,
*kwargs, **kwargs)
self.shrink = True
self.link = True
self.link_func = self.link_to_object
def render_data(self, review_request):
return str(review_request.get_public_reviews().count())
def link_to_object(self, review_request, value):
return "%s#last-review" % review_request.get_absolute_url()
class ReviewRequestDataGrid(DataGrid):
"""
A datagrid that displays a list of review requests.
This datagrid accepts the show_submitted parameter in the URL, allowing
submitted review requests to be filtered out or displayed.
"""
star = StarColumn()
ship_it = ShipItColumn()
summary = SummaryColumn(expand=True, link=True, css_class="summary")
submitter = Column(_("Submitter"), db_field="submitter__username",
shrink=True, sortable=True, link=True)
repository = Column(_("Repository"), db_field="repository__name",
shrink=True, sortable=True, link=False)
time_added = DateTimeColumn(_("Posted"),
detailed_label=_("Posted Time"),
format="F jS, Y, P", shrink=True,
css_class=lambda r: ageid(r.time_added))
last_updated = DateTimeColumn(_("Last Updated"),
format="F jS, Y, P", shrink=True,
db_field="last_updated",
field_name="last_updated",
css_class=lambda r: ageid(r.last_updated))
diff_updated = DateTimeColumn(_("Diff Updated"),
format="F jS, Y, P", shrink=True,
field_name="last_updated",
css_class=lambda r: ageid(r.last_updated))
time_added_since = DateTimeSinceColumn(_("Posted"),
detailed_label=_("Posted Time (Relative)"),
field_name="time_added", shrink=True,
css_class=lambda r: ageid(r.time_added))
last_updated_since = DateTimeSinceColumn(_("Last Updated"),
detailed_label=_("Last Updated (Relative)"), shrink=True,
db_field="last_updated",
field_name="last_updated",
css_class=lambda r: ageid(r.last_updated))
diff_updated_since = DateTimeSinceColumn(_("Diff Updated"),
detailed_label=_("Diff Updated (Relative)"),
field_name="last_updated", shrink=True,
css_class=lambda r: ageid(r.last_updated))
review_count = ReviewCountColumn()
review_id = Column(_("Review ID"), field_name="id", db_field="id",
shrink=True, sortable=True, link=True)
def __init__(self, *args, **kwargs):
DataGrid.__init__(self, *args, **kwargs)
self.listview_template = 'reviews/review_request_listview.html'
self.profile_sort_field = 'sort_review_request_columns'
self.profile_columns_field = 'review_request_columns'
self.show_submitted = True
self.submitter_url_name = "user"
self.default_sort = ["-last_updated"]
self.default_columns = [
"star", "summary", "submitter", "time_added", "last_updated_since"
]
def load_extra_state(self, profile):
if profile:
self.show_submitted = profile.show_submitted
self.show_submitted = \
int(self.request.GET.get('show_submitted',
self.show_submitted)) != 0
if self.show_submitted:
# There are only three states: Published, Submitted and Discarded.
# We want the first two, but it's faster to just search for not
# discarded.
self.queryset = self.queryset.exclude(status='D')
else:
self.queryset = self.queryset.filter(status='P')
if profile and self.show_submitted != profile.show_submitted:
profile.show_submitted = self.show_submitted
return True
return False
def post_process_queryset(self, queryset):
return queryset.with_counts(self.request.user)
def link_to_object(self, obj, value):
if value and isinstance(value, User):
return reverse("user", args=[value])
return obj.get_absolute_url()
class DashboardDataGrid(ReviewRequestDataGrid):
"""
A version of the ReviewRequestDataGrid that displays additional fields
useful in the dashboard. It also displays a different set of data
depending on the view that was passed.
"""
new_updates = NewUpdatesColumn()
my_comments = MyCommentsColumn()
def __init__(self, *args, **kwargs):
ReviewRequestDataGrid.__init__(self, *args, **kwargs)
self.listview_template = 'datagrid/listview.html'
self.profile_sort_field = 'sort_dashboard_columns'
self.profile_columns_field = 'dashboard_columns'
self.default_view = "incoming"
self.show_submitted = False
self.default_sort = ["-last_updated"]
self.default_columns = [
"new_updates", "star", "summary", "submitter",
"time_added", "last_updated_since"
]
group = self.request.GET.get('group', None)
view = self.request.GET.get('view', None)
extra_query = []
if view:
extra_query.append("view=%s" % view)
if group:
extra_query.append("group=%s" % group)
self.extra_context['extra_query'] = "&".join(extra_query)
def load_extra_state(self, profile):
group = self.request.GET.get('group', '')
view = self.request.GET.get('view', self.default_view)
user = self.request.user
if view == 'outgoing':
self.queryset = ReviewRequest.objects.from_user(user.username,
user,
with_counts=True)
self.title = _(u"All Outgoing Review Requests")
elif view == 'mine':
self.queryset = ReviewRequest.objects.from_user(user.username, user,
None,
with_counts=True)
self.title = _(u"All My Review Requests")
elif view == 'to-me':
self.queryset = \
ReviewRequest.objects.to_user_directly(user.username, user,
with_counts=True)
self.title = _(u"Incoming Review Requests to Me")
elif view == 'to-group':
if group != "":
self.queryset = ReviewRequest.objects.to_group(group, user,
with_counts=True)
self.title = _(u"Incoming Review Requests to %s") % group
else:
self.queryset = \
ReviewRequest.objects.to_user_groups(user.username, user,
with_counts=True)
self.title = _(u"All Incoming Review Requests to My Groups")
elif view == 'starred':
profile = user.get_profile()
self.queryset = \
profile.starred_review_requests.public(user, with_counts=True)
self.title = _(u"Starred Review Requests")
else: # "incoming" or invalid
self.queryset = ReviewRequest.objects.to_user(user.username, user,
with_counts=True)
self.title = _(u"All Incoming Review Requests")
return False
class SubmitterDataGrid(DataGrid):
"""
A datagrid showing a list of submitters.
"""
username = Column(_("Username"), link=True, sortable=True)
fullname = Column(_("Full Name"), field_name="get_full_name",
link=True, expand=True)
pending_count = PendingCountColumn(_("Pending Reviews"),
field_name="directed_review_requests",
shrink=True)
def __init__(self, request,
queryset=User.objects.filter(is_active=True),
title=_("All submitters")):
DataGrid.__init__(self, request, queryset, title)
self.default_sort = ["username"]
self.profile_sort_field = 'sort_submitter_columns'
self.profile_columns_field = 'submitter_columns'
self.default_columns = [
"username", "fullname", "pending_count"
]
@staticmethod
def link_to_object(obj, value):
return reverse("user", args=[obj.username])
class GroupDataGrid(DataGrid):
"""
A datagrid showing a list of review groups.
"""
star = StarColumn()
name = Column(_("Group ID"), link=True, sortable=True)
displayname = Column(_("Group Name"), field_name="display_name",
link=True, expand=True)
pending_count = PendingCountColumn(_("Pending Reviews"),
field_name="review_requests",
link=True,
shrink=True)
member_count = GroupMemberCountColumn(_("Members"),
field_name="members",
shrink=True)
def __init__(self, request, title=_("All groups"), *args, **kwargs):
DataGrid.__init__(self, request, queryset=Group.objects.all(),
title=title, *args, **kwargs)
self.profile_sort_field = 'sort_group_columns'
self.profile_columns_field = 'group_columns'
self.default_sort = ["name"]
self.default_columns = [
"star", "name", "displayname", "pending_count"
]
@staticmethod
def link_to_object(obj, value):
return reverse("group", args=[obj.name])
class WatchedGroupDataGrid(GroupDataGrid):
"""
A special version of GroupDataGrid that shows a list of watched groups,
linking to a dashboard view of them. This is meant for display in the
dashboard.
"""
def __init__(self, request, title=_("Watched groups"), *args, **kwargs):
GroupDataGrid.__init__(self, request, title=title, *args, **kwargs)
self.queryset = request.user.get_profile().starred_groups.all()
def link_to_object(self, group, value):
return ".?view=to-group&group=%s" % group.name