| Index: appengine/monorail/tracker/issuelistcsv.py
|
| diff --git a/appengine/monorail/tracker/issuelistcsv.py b/appengine/monorail/tracker/issuelistcsv.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f00c6eebcbe271c9b72634031e236b578e392f96
|
| --- /dev/null
|
| +++ b/appengine/monorail/tracker/issuelistcsv.py
|
| @@ -0,0 +1,85 @@
|
| +# 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
|
| +
|
| +"""Implemention of the issue list output as a CSV file."""
|
| +
|
| +import settings
|
| +from framework import framework_helpers
|
| +from framework import permissions
|
| +from framework import urls
|
| +from tracker import issuelist
|
| +from tracker import tablecell
|
| +from tracker import tracker_constants
|
| +
|
| +
|
| +class IssueListCsv(issuelist.IssueList):
|
| + """IssueListCsv provides to the user a list of issues as a CSV document.
|
| +
|
| + Overrides the standard IssueList servlet but uses a different EZT template
|
| + to provide the same content as the IssueList only as CSV. Adds the HTTP
|
| + header to offer the result as a download.
|
| + """
|
| +
|
| + _PAGE_TEMPLATE = 'tracker/issue-list-csv.ezt'
|
| + _DEFAULT_RESULTS_PER_PAGE = settings.max_artifact_search_results_per_page
|
| +
|
| + def GatherPageData(self, mr):
|
| + if not mr.auth.user_id:
|
| + raise permissions.PermissionException(
|
| + 'Anonymous users are not allowed to download issue list CSV')
|
| +
|
| + # Sets headers to allow the response to be downloaded.
|
| + self.content_type = 'text/csv; charset=UTF-8'
|
| + download_filename = '%s-issues.csv' % mr.project_name
|
| + self.response.headers.add(
|
| + 'Content-Disposition', 'attachment; filename=%s' % download_filename)
|
| + self.response.headers.add('X-Content-Type-Options', 'nosniff')
|
| +
|
| + # Rewrite the colspec to add some extra columns that make the CSV
|
| + # file more complete.
|
| + with self.profiler.Phase('finishing config work'):
|
| + config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
|
| +
|
| + mr.ComputeColSpec(config)
|
| + mr.col_spec = _RewriteColspec(mr.col_spec)
|
| + page_data = issuelist.IssueList.GatherPageData(self, mr)
|
| +
|
| + # CSV files are at risk for PDF content sniffing by Acrobat Reader.
|
| + page_data['prevent_sniffing'] = True
|
| +
|
| + # If we're truncating the results, add a URL to the next page of results
|
| + page_data['next_csv_link'] = None
|
| + pagination = page_data['pagination']
|
| + if pagination.next_url:
|
| + page_data['next_csv_link'] = framework_helpers.FormatAbsoluteURL(
|
| + mr, urls.ISSUE_LIST_CSV, start=pagination.last)
|
| + page_data['item_count'] = pagination.last - pagination.start + 1
|
| +
|
| + return page_data
|
| +
|
| + def GetCellFactories(self):
|
| + return tablecell.CSV_CELL_FACTORIES
|
| +
|
| +
|
| +# Whenever the user request one of these columns, we replace it with the
|
| +# list of alternate columns. In effect, we split the requested column
|
| +# into two CSV columns.
|
| +_CSV_COLS_TO_REPLACE = {
|
| + 'summary': ['Summary', 'AllLabels'],
|
| + 'opened': ['Opened', 'OpenedTimestamp'],
|
| + 'closed': ['Closed', 'ClosedTimestamp'],
|
| + 'modified': ['Modified', 'ModifiedTimestamp'],
|
| + }
|
| +
|
| +
|
| +def _RewriteColspec(col_spec):
|
| + """Rewrite the given colspec to expand special CSV columns."""
|
| + new_cols = []
|
| +
|
| + for col in col_spec.split():
|
| + rewriten_cols = _CSV_COLS_TO_REPLACE.get(col.lower(), [col])
|
| + new_cols.extend(rewriten_cols)
|
| +
|
| + return ' '.join(new_cols)
|
|
|