blob: 356bb156680d0010eae1f52b3db579f8abbf4558 [file] [log] [blame]
# Copyright 2018 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
"""Helper functions for issue template servlets"""
import collections
import logging
from framework import authdata
from framework import exceptions
from framework import framework_bizobj
from framework import framework_helpers
from tracker import field_helpers
from tracker import tracker_bizobj
from tracker import tracker_helpers
from proto import tracker_pb2
PHASE_INPUTS = [
'phase_0', 'phase_1', 'phase_2', 'phase_3', 'phase_4', 'phase_5']
ParsedTemplate = collections.namedtuple(
'ParsedTemplate', 'name, members_only, summary, summary_must_be_edited, '
'content, status, owner_str, labels, field_val_strs, component_paths, '
'component_required, owner_defaults_to_member, admin_str, add_phases, '
'phase_names, approvals_by_phase_idx')
def ParseTemplateRequest(post_data, config):
"""Parse an issue template."""
name = post_data.get('name', '')
members_only = (post_data.get('members_only') == 'on')
summary = post_data.get('summary', '')
summary_must_be_edited = (
post_data.get('summary_must_be_edited') == 'on')
content = post_data.get('content', '')
content = framework_helpers.WordWrapSuperLongLines(content, max_cols=75)
status = post_data.get('status', '')
owner_str = post_data.get('owner', '')
labels = post_data.getall('label')
field_val_strs = collections.defaultdict(list)
for fd in config.field_defs:
field_value_key = 'custom_%d' % fd.field_id
if post_data.get(field_value_key):
field_val_strs[fd.field_id].append(post_data[field_value_key])
component_paths = []
if post_data.get('components'):
for component_path in post_data.get('components').split(','):
if component_path.strip() not in component_paths:
component_paths.append(component_path.strip())
component_required = post_data.get('component_required') == 'on'
owner_defaults_to_member = post_data.get('owner_defaults_to_member') == 'on'
admin_str = post_data.get('admin_names', '')
add_phases = post_data.get('add_phases') == 'on'
phase_names = [post_data.get(phase_input, '') for phase_input in PHASE_INPUTS]
approvals_by_phase_idx = collections.defaultdict(list)
for approval_def in config.approval_defs:
phase_num = post_data.get('approval_%d' % approval_def.approval_id, '')
try:
idx = PHASE_INPUTS.index(phase_num)
approvals_by_phase_idx[idx].append(approval_def.approval_id)
except ValueError:
logging.info('approval %d was omitted' % approval_def.approval_id)
return ParsedTemplate(
name, members_only, summary, summary_must_be_edited, content, status,
owner_str, labels, field_val_strs, component_paths, component_required,
owner_defaults_to_member, admin_str, add_phases, phase_names,
approvals_by_phase_idx)
def GetTemplateInfoFromParsed(mr, services, parsed, config):
"""Get Template field info and PBs from a ParsedTemplate."""
admin_ids, _ = tracker_helpers.ParseAdminUsers(
mr.cnxn, parsed.admin_str, services.user)
owner_id = 0
if parsed.owner_str:
try:
user_id = services.user.LookupUserID(mr.cnxn, parsed.owner_str)
auth = authdata.AuthData.FromUserID(mr.cnxn, user_id, services)
if framework_bizobj.UserIsInProject(mr.project, auth.effective_ids):
owner_id = user_id
else:
mr.errors.owner = 'User is not a member of this project.'
except exceptions.NoSuchUserException:
mr.errors.owner = 'Owner not found.'
component_ids = tracker_helpers.LookupComponentIDs(
parsed.component_paths, config, mr.errors)
field_values = field_helpers.ParseFieldValues(
mr.cnxn, services.user, parsed.field_val_strs, config)
for fv in field_values:
logging.info('field_value is %r: %r',
fv.field_id, tracker_bizobj.GetFieldValue(fv, {}))
phases = []
if parsed.add_phases:
phases = _GetPhasesFromParsed(
mr, parsed.phase_names, parsed.approvals_by_phase_idx)
return admin_ids, owner_id, component_ids, field_values, phases
def _GetPhasesFromParsed(mr, phase_names, approvals_by_phase_idx):
"""Get Phase PBs from a parsed phase_names and approvals_by_phase_idx."""
phases = []
valid_phase_idxs = [idx for idx, name in enumerate(phase_names) if name]
if set(valid_phase_idxs) != set(approvals_by_phase_idx.keys()):
mr.errors.phase_approvals = 'Defined gates must have assigned approvals.'
return phases
for idx in approvals_by_phase_idx:
approval_ids = approvals_by_phase_idx[idx]
phase_name = phase_names[idx]
# Distributing the ranks over a wider range is not necessary since
# any edits to template phases will cause a complete rewrite
phase = tracker_pb2.Phase(name=phase_name, rank=idx)
phases.append(phase)
for approval_id in approval_ids:
av = tracker_pb2.ApprovalValue(
approval_id=approval_id)
# TODO(jojwang): monorail:3655, add default sub_field_values
# TODO(jojwang): monorail:3656, add option for default approvers
# per template
phase.approval_values.append(av)
return phases