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

Unified Diff: appengine/monorail/tracker/issueexport.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/issueentry.py ('k') | appengine/monorail/tracker/issueimport.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/monorail/tracker/issueexport.py
diff --git a/appengine/monorail/tracker/issueexport.py b/appengine/monorail/tracker/issueexport.py
new file mode 100644
index 0000000000000000000000000000000000000000..4965193b4568ed2b70719b76fe762ca6722897ba
--- /dev/null
+++ b/appengine/monorail/tracker/issueexport.py
@@ -0,0 +1,205 @@
+# 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
+
+"""Servlet to export a range of issues in JSON format.
+"""
+
+import logging
+import time
+
+from third_party import ezt
+
+from framework import permissions
+from framework import jsonfeed
+from framework import servlet
+from tracker import tracker_bizobj
+
+
+class IssueExport(servlet.Servlet):
+ """IssueExportControls let's an admin choose how to export issues."""
+
+ _PAGE_TEMPLATE = 'tracker/issue-export-page.ezt'
+ _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_ISSUES
+
+ def AssertBasePermission(self, mr):
+ """Make sure that the logged in user has permission to view this page."""
+ super(IssueExport, self).AssertBasePermission(mr)
+ if not mr.auth.user_pb.is_site_admin:
+ raise permissions.PermissionException(
+ 'Only site admins may export issues')
+
+ def GatherPageData(self, mr):
+ """Build up a dictionary of data values to use when rendering the page."""
+
+ return {
+ 'issue_tab_mode': None,
+ 'initial_start': mr.start,
+ 'initial_num': mr.num,
+ 'page_perms': self.MakePagePerms(mr, None, permissions.CREATE_ISSUE),
+ }
+
+
+class IssueExportJSON(jsonfeed.JsonFeed):
+ """IssueExport shows a range of issues in JSON format."""
+
+ # Pretty-print the JSON output.
+ JSON_INDENT = 4
+
+ def AssertBasePermission(self, mr):
+ """Make sure that the logged in user has permission to view this page."""
+ super(IssueExportJSON, self).AssertBasePermission(mr)
+ if not mr.auth.user_pb.is_site_admin:
+ raise permissions.PermissionException(
+ 'Only site admins may export issues')
+
+ def HandleRequest(self, mr):
+ """Build up a dictionary of data values to use when rendering the page.
+
+ Args:
+ mr: commonly used info parsed from the request.
+
+ Returns:
+ Dict of values used by EZT for rendering the page.
+ """
+ if not mr.start and not mr.num:
+ issues = self.services.issue.GetAllIssuesInProject(
+ mr.cnxn, mr.project.project_id)
+ else:
+ local_id_range = range(mr.start, mr.start + mr.num)
+ issues = self.services.issue.GetIssuesByLocalIDs(
+ mr.cnxn, mr.project.project_id, local_id_range)
+ user_id_set = tracker_bizobj.UsersInvolvedInIssues(issues)
+
+ comments_dict = self.services.issue.GetCommentsForIssues(
+ mr.cnxn, [issue.issue_id for issue in issues])
+ for comment_list in comments_dict.itervalues():
+ user_id_set.update(
+ tracker_bizobj.UsersInvolvedInCommentList(comment_list))
+
+ starrers_dict = self.services.issue_star.LookupItemsStarrers(
+ mr.cnxn, [issue.issue_id for issue in issues])
+ for starrer_id_list in starrers_dict.itervalues():
+ user_id_set.update(starrer_id_list)
+
+ # The value 0 indicates "no user", e.g., that an issue has no owner.
+ # We don't need to create a User row to represent that.
+ user_id_set.discard(0)
+ email_dict = self.services.user.LookupUserEmails(mr.cnxn, user_id_set)
+
+ issues_json = [
+ self._MakeIssueJSON(
+ mr, issue, email_dict,
+ comments_dict.get(issue.issue_id, []),
+ starrers_dict.get(issue.issue_id, []))
+ for issue in issues if not issue.deleted]
+
+ json_data = {
+ 'metadata': {
+ 'version': 1,
+ 'when': int(time.time()),
+ 'who': mr.auth.email,
+ 'project': mr.project_name,
+ 'start': mr.start,
+ 'num': mr.num,
+ },
+ 'issues': issues_json,
+ # This list could be derived from the 'issues', but we provide it for
+ # ease of processing.
+ 'emails': email_dict.values(),
+ }
+ return json_data
+
+ def _MakeAmendmentJSON(self, amendment, email_dict):
+ amendment_json = {
+ 'field': amendment.field.name,
+ }
+ if amendment.custom_field_name:
+ amendment_json.update({'custom_field_name': amendment.custom_field_name})
+ if amendment.newvalue:
+ amendment_json.update({'new_value': amendment.newvalue})
+ if amendment.added_user_ids:
+ amendment_json.update(
+ {'added_emails': [email_dict.get(user_id)
+ for user_id in amendment.added_user_ids]})
+ if amendment.removed_user_ids:
+ amendment_json.update(
+ {'removed_emails': [email_dict.get(user_id)
+ for user_id in amendment.removed_user_ids]})
+ return amendment_json
+
+ def _MakeAttachmentJSON(self, attachment):
+ if attachment.deleted:
+ return None
+ attachment_json = {
+ 'name': attachment.filename,
+ 'size': attachment.filesize,
+ 'mimetype': attachment.mimetype,
+ 'gcs_object_id': attachment.gcs_object_id,
+ }
+ return attachment_json
+
+ def _MakeCommentJSON(self, comment, email_dict):
+ if comment.deleted_by:
+ return None
+ amendments = [self._MakeAmendmentJSON(a, email_dict)
+ for a in comment.amendments]
+ attachments = [self._MakeAttachmentJSON(a)
+ for a in comment.attachments]
+ comment_json = {
+ 'timestamp': comment.timestamp,
+ 'commenter': email_dict.get(comment.user_id),
+ 'content': comment.content,
+ 'amendments': [a for a in amendments if a],
+ 'attachments': [a for a in attachments if a],
+ }
+ return comment_json
+
+ def _MakeFieldValueJSON(self, mr, field, email_dict):
+ field_value_json = {
+ 'field': self.services.config.LookupField(
+ mr.cnxn, mr.project.project_id, field.field_id)
+ }
+ if field.int_value:
+ field_value_json['int_value'] = field.int_value
+ if field.str_value:
+ field_value_json['str_value'] = field.str_value
+ if field.user_id:
+ field_value_json['user_value'] = email_dict.get(field.user_id)
+ return field_value_json
+
+ def _MakeIssueJSON(
+ self, mr, issue, email_dict, comment_list, starrer_id_list):
+ """Return a dict of info about the issue and its comments."""
+ comments = [self._MakeCommentJSON(c, email_dict) for c in comment_list]
+ issue_json = {
+ 'local_id': issue.local_id,
+ 'reporter': email_dict.get(issue.reporter_id),
+ 'summary': issue.summary,
+ 'owner': email_dict.get(issue.owner_id),
+ 'status': issue.status,
+ 'cc': [email_dict[cc_id] for cc_id in issue.cc_ids],
+ 'labels': issue.labels,
+ 'fields': [self._MakeFieldValueJSON(mr, field, email_dict)
+ for field in issue.field_values],
+ 'starrers': [email_dict[starrer] for starrer in starrer_id_list],
+ 'comments': [c for c in comments if c],
+ 'opened': issue.opened_timestamp,
+ 'modified': issue.modified_timestamp,
+ 'closed': issue.closed_timestamp,
+ }
+ # TODO(agable): Export cross-project references as well.
+ if issue.blocked_on_iids:
+ issue_json['blocked_on'] = [i.local_id for i in
+ self.services.issue.GetIssues(mr.cnxn, issue.blocked_on_iids)
+ if i.project_id == mr.project.project_id]
+ if issue.blocking_iids:
+ issue_json['blocking'] = [i.local_id for i in
+ self.services.issue.GetIssues(mr.cnxn, issue.blocking_iids)
+ if i.project_id == mr.project.project_id]
+ if issue.merged_into:
+ merge = self.services.issue.GetIssue(mr.cnxn, issue.merged_into)
+ if merge.project_id == mr.project.project_id:
+ issue_json['merged_into'] = merge.local_id
+ return issue_json
« no previous file with comments | « appengine/monorail/tracker/issueentry.py ('k') | appengine/monorail/tracker/issueimport.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698