Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2691)

Unified Diff: appengine/monorail/tracker/tablecell.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « appengine/monorail/tracker/spam_helpers.py ('k') | appengine/monorail/tracker/test/__init__.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/monorail/tracker/tablecell.py
diff --git a/appengine/monorail/tracker/tablecell.py b/appengine/monorail/tracker/tablecell.py
new file mode 100644
index 0000000000000000000000000000000000000000..ad84a89a66c35f6ab6814ca4c87c472e5bcc9137
--- /dev/null
+++ b/appengine/monorail/tracker/tablecell.py
@@ -0,0 +1,422 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is govered by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""Classes that generate value cells in the issue list table."""
+
+import logging
+import time
+from third_party import ezt
+
+from framework import table_view_helpers
+from framework import timestr
+from tracker import tracker_bizobj
+
+# pylint: disable=unused-argument
+
+
+class TableCellID(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue IDs."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ID, [str(issue.local_id)])
+
+
+class TableCellStatus(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue status values."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ values = []
+ derived_values = []
+ if issue.status:
+ values = [issue.status]
+ if issue.derived_status:
+ derived_values = [issue.derived_status]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values,
+ derived_values=derived_values)
+
+
+class TableCellOwner(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue owner name."""
+
+ # Make instances of this class render with whitespace:nowrap.
+ NOWRAP = ezt.boolean(True)
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ values = []
+ derived_values = []
+ if issue.owner_id:
+ values = [users_by_id[issue.owner_id].display_name]
+ if issue.derived_owner_id:
+ derived_values = [users_by_id[issue.derived_owner_id].display_name]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values,
+ derived_values=derived_values)
+
+
+class TableCellReporter(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue reporter name."""
+
+ # Make instances of this class render with whitespace:nowrap.
+ NOWRAP = ezt.boolean(True)
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ try:
+ values = [users_by_id[issue.reporter_id].display_name]
+ except KeyError:
+ logging.info('issue reporter %r not found', issue.reporter_id)
+ values = ['deleted?']
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values)
+
+
+class TableCellCc(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue Cc user names."""
+
+ def __init__(
+ self, issue, _col, users_by_id, _non_col_labels,
+ _label_values, _related_issues, _config):
+ values = [users_by_id[cc_id].display_name
+ for cc_id in issue.cc_ids]
+
+ derived_values = [users_by_id[cc_id].display_name
+ for cc_id in issue.derived_cc_ids]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values,
+ derived_values=derived_values)
+
+
+class TableCellAttachments(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue attachment count."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, [issue.attachment_count],
+ align='right')
+
+
+class TableCellOpened(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue opened date."""
+
+ # Make instances of this class render with whitespace:nowrap.
+ NOWRAP = ezt.boolean(True)
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ date_str = timestr.FormatRelativeDate(
+ issue.opened_timestamp, recent_only=True)
+ if not date_str:
+ date_str = timestr.FormatAbsoluteDate(issue.opened_timestamp)
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE, [date_str])
+
+
+class TableCellClosed(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue closed date."""
+
+ # Make instances of this class render with whitespace:nowrap.
+ NOWRAP = ezt.boolean(True)
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ values = []
+ if issue.closed_timestamp:
+ date_str = timestr.FormatRelativeDate(
+ issue.closed_timestamp, recent_only=True)
+ if not date_str:
+ date_str = timestr.FormatAbsoluteDate(issue.closed_timestamp)
+ values = [date_str]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE, values)
+
+
+class TableCellModified(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue modified date."""
+
+ # Make instances of this class render with whitespace:nowrap.
+ NOWRAP = ezt.boolean(True)
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ values = []
+ if issue.modified_timestamp:
+ date_str = timestr.FormatRelativeDate(
+ issue.modified_timestamp, recent_only=True)
+ if not date_str:
+ date_str = timestr.FormatAbsoluteDate(issue.modified_timestamp)
+ values = [date_str]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE, values)
+
+
+class TableCellBlockedOn(table_view_helpers.TableCell):
+ """TableCell subclass for listing issues the current issue is blocked on."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ related_issues, _config):
+ ref_issues = [related_issues[iid] for iid in issue.blocked_on_iids
+ if iid in related_issues]
+ default_pn = issue.project_name
+ # TODO(jrobbins): in cross-project searches, leave default_pn = None.
+ values = [
+ tracker_bizobj.FormatIssueRef(
+ (ref_issue.project_name, ref_issue.local_id),
+ default_project_name=default_pn)
+ for ref_issue in ref_issues]
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values)
+
+
+class TableCellBlocking(table_view_helpers.TableCell):
+ """TableCell subclass for listing issues the current issue is blocking."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ related_issues, _config):
+ ref_issues = [related_issues[iid] for iid in issue.blocking_iids
+ if iid in related_issues]
+ default_pn = issue.project_name
+ # TODO(jrobbins): in cross-project searches, leave default_pn = None.
+ values = [
+ tracker_bizobj.FormatIssueRef(
+ (ref_issue.project_name, ref_issue.local_id),
+ default_project_name=default_pn)
+ for ref_issue in ref_issues]
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values)
+
+
+class TableCellBlocked(table_view_helpers.TableCell):
+ """TableCell subclass for showing whether an issue is blocked."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related_issues, _config):
+ if issue.blocked_on_iids:
+ value = 'Yes'
+ else:
+ value = 'No'
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, [value])
+
+
+class TableCellMergedInto(table_view_helpers.TableCell):
+ """TableCell subclass for showing whether an issue is blocked."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ related_issues, _config):
+ if issue.merged_into:
+ ref_issue = related_issues[issue.merged_into]
+ ref = ref_issue.project_name, ref_issue.local_id
+ default_pn = issue.project_name
+ # TODO(jrobbins): in cross-project searches, leave default_pn = None.
+ values = [
+ tracker_bizobj.FormatIssueRef(ref, default_project_name=default_pn)]
+ else: # Note: None means not merged into any issue.
+ values = []
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values)
+
+
+class TableCellComponent(table_view_helpers.TableCell):
+ """TableCell subclass for showing components."""
+
+ def __init__(
+ self, issue, _col, _users_by_id, _non_col_labels,
+ _label_values, _related_issues, config):
+ explicit_paths = []
+ for component_id in issue.component_ids:
+ cd = tracker_bizobj.FindComponentDefByID(component_id, config)
+ if cd:
+ explicit_paths.append(cd.path)
+
+ derived_paths = []
+ for component_id in issue.derived_component_ids:
+ cd = tracker_bizobj.FindComponentDefByID(component_id, config)
+ if cd:
+ derived_paths.append(cd.path)
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, explicit_paths,
+ derived_values=derived_paths)
+
+
+# This maps column names to factories/constructors that make table cells.
+# Subclasses can override this mapping, so any additions to this mapping
+# should also be added to subclasses.
+CELL_FACTORIES = {
+ 'id': TableCellID,
+ 'project': table_view_helpers.TableCellProject,
+ 'component': TableCellComponent,
+ 'summary': table_view_helpers.TableCellSummary,
+ 'status': TableCellStatus,
+ 'owner': TableCellOwner,
+ 'reporter': TableCellReporter,
+ 'cc': TableCellCc,
+ 'stars': table_view_helpers.TableCellStars,
+ 'attachments': TableCellAttachments,
+ 'opened': TableCellOpened,
+ 'closed': TableCellClosed,
+ 'modified': TableCellModified,
+ 'blockedon': TableCellBlockedOn,
+ 'blocking': TableCellBlocking,
+ 'blocked': TableCellBlocked,
+ 'mergedinto': TableCellMergedInto,
+ }
+
+
+# Time format that spreadsheets seem to understand.
+# E.g.: "May 19 2008 13:30:23". Tested with MS Excel 2003,
+# OpenOffice.org, NeoOffice, and Google Spreadsheets.
+CSV_DATE_TIME_FMT = '%b %d, %Y %H:%M:%S'
+
+
+def TimeStringForCSV(timestamp):
+ """Return a timestamp in a format that spreadsheets understand."""
+ return time.strftime(CSV_DATE_TIME_FMT, time.gmtime(timestamp))
+
+
+class TableCellSummaryCSV(table_view_helpers.TableCell):
+ """TableCell subclass for showing issue summaries escaped for CSV."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ escaped_summary = issue.summary.replace('"', '""')
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_SUMMARY, [escaped_summary],
+ non_column_labels=non_col_labels)
+
+
+class TableCellAllLabels(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing all labels on an issue."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ values = []
+ derived_values = []
+ if issue.labels:
+ values = issue.labels[:]
+ if issue.derived_labels:
+ derived_values = issue.derived_labels[:]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_ATTR, values,
+ derived_values=derived_values)
+
+
+class TableCellOpenedCSV(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue opened date."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ date_str = TimeStringForCSV(issue.opened_timestamp)
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE, [date_str])
+
+
+class TableCellOpenedTimestamp(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue opened timestamp."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE,
+ [issue.opened_timestamp])
+
+
+class TableCellModifiedCSV(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue modified date."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ values = []
+ if issue.modified_timestamp:
+ values = [TimeStringForCSV(issue.modified_timestamp)]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE, values)
+
+
+class TableCellModifiedTimestamp(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue modified timestamp."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE,
+ [issue.modified_timestamp])
+
+
+class TableCellClosedCSV(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue closed date."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ values = []
+ if issue.closed_timestamp:
+ values = [TimeStringForCSV(issue.closed_timestamp)]
+
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE, values)
+
+
+class TableCellClosedTimestamp(table_view_helpers.TableCell):
+ """TableCell subclass specifically for showing issue closed timestamp."""
+
+ def __init__(
+ self, issue, col, users_by_id, non_col_labels, label_values,
+ _related, _config):
+ table_view_helpers.TableCell.__init__(
+ self, table_view_helpers.CELL_TYPE_UNFILTERABLE,
+ [issue.closed_timestamp])
+
+
+# Maps column names to factories/constructors that make table cells.
+# Uses the defaults in issuelist.py but changes the factory for the
+# summary cell to properly escape the data for CSV files.
+CSV_CELL_FACTORIES = CELL_FACTORIES.copy()
+CSV_CELL_FACTORIES.update({
+ 'summary': TableCellSummaryCSV,
+ 'alllabels': TableCellAllLabels,
+ 'opened': TableCellOpenedCSV,
+ 'openedtimestamp': TableCellOpenedTimestamp,
+ 'closed': TableCellClosedCSV,
+ 'closedtimestamp': TableCellClosedTimestamp,
+ 'modified': TableCellModifiedCSV,
+ 'modifiedtimestamp': TableCellModifiedTimestamp,
+ })
« no previous file with comments | « appengine/monorail/tracker/spam_helpers.py ('k') | appengine/monorail/tracker/test/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698