| # Copyright 2018 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style |
| # license that can be found in the LICENSE file or at |
| # https://developers.google.com/open-source/licenses/bsd |
| |
| """FLT task to be manually triggered to convert launch issues.""" |
| from __future__ import print_function |
| from __future__ import division |
| from __future__ import absolute_import |
| |
| import collections |
| import logging |
| import re |
| import settings |
| import time |
| |
| from businesslogic import work_env |
| from framework import permissions |
| from framework import exceptions |
| from framework import jsonfeed |
| from proto import tracker_pb2 |
| from tracker import template_helpers |
| from tracker import tracker_bizobj |
| |
| PM_PREFIX = 'pm-' |
| TL_PREFIX = 'tl-' |
| TEST_PREFIX = 'test-' |
| UX_PREFIX = 'ux-' |
| |
| PM_FIELD = 'pm' |
| TL_FIELD = 'tl' |
| TE_FIELD = 'te' |
| UX_FIELD = 'ux' |
| MTARGET_FIELD = 'm-target' |
| MAPPROVED_FIELD = 'm-approved' |
| |
| CONVERSION_COMMENT = 'Automatic generating of FLT Launch data.' |
| |
| BROWSER_APPROVALS_TO_LABELS = { |
| 'Chrome-Accessibility': 'Launch-Accessibility-', |
| 'Chrome-Leadership-Exp': 'Launch-Exp-Leadership-', |
| 'Chrome-Leadership-Full': 'Launch-Leadership-', |
| 'Chrome-Legal': 'Launch-Legal-', |
| 'Chrome-Privacy': 'Launch-Privacy-', |
| 'Chrome-Security': 'Launch-Security-', |
| 'Chrome-Test': 'Launch-Test-', |
| 'Chrome-UX': 'Launch-UI-', |
| } |
| |
| OS_APPROVALS_TO_LABELS = { |
| 'ChromeOS-Accessibility': 'Launch-Accessibility-', |
| 'ChromeOS-Leadership-Exp': 'Launch-Exp-Leadership-', |
| 'ChromeOS-Leadership-Full': 'Launch-Leadership-', |
| 'ChromeOS-Legal': 'Launch-Legal-', |
| 'ChromeOS-Privacy': 'Launch-Privacy-', |
| 'ChromeOS-Security': 'Launch-Security-', |
| 'ChromeOS-Test': 'Launch-Test-', |
| 'ChromeOS-UX': 'Launch-UI-', |
| } |
| |
| # 'NotReviewed' not included because this should be converted to |
| # the template approval's default value, eg NOT_SET OR NEEDS_REVIEW |
| VALUE_TO_STATUS = { |
| 'ReviewRequested': tracker_pb2.ApprovalStatus.REVIEW_REQUESTED, |
| 'NeedInfo': tracker_pb2.ApprovalStatus.NEED_INFO, |
| 'Yes': tracker_pb2.ApprovalStatus.APPROVED, |
| 'No': tracker_pb2.ApprovalStatus.NOT_APPROVED, |
| 'NA': tracker_pb2.ApprovalStatus.NA, |
| # 'Started' is not a valid label value in the chromium project, |
| # but for some reason, some labels have this value. |
| 'Started': tracker_pb2.ApprovalStatus.REVIEW_STARTED, |
| } |
| |
| # This works in the Browser and OS process because |
| # BROWSER_APPROVALS_TO_LABELS and OS_APPROVALS_TO_LABELS have the same values. |
| # Adding '^' before each label prefix to ensure Blah-Launch-UI-Yes is ignored |
| REVIEW_LABELS_RE = re.compile('^' + '|^'.join( |
| list(OS_APPROVALS_TO_LABELS.values()))) |
| |
| # Maps template phases to channel names in 'Launch-M-Target-80-[Channel]' labels |
| BROWSER_PHASE_MAP = { |
| 'beta': 'beta', |
| 'stable': 'stable', |
| 'stable-full': 'stable', |
| 'stable-exp': 'stable-exp', |
| } |
| |
| PHASE_PAT = '$|'.join(list(BROWSER_PHASE_MAP.values())) |
| # Matches launch milestone labels, eg. Launch-M-Target-70-Stable-Exp |
| BROWSER_M_LABELS_RE = re.compile( |
| r'^Launch-M-(?P<type>Approved|Target)-(?P<m>\d\d)-' |
| r'(?P<channel>%s$)' % PHASE_PAT, |
| re.IGNORECASE) |
| |
| OS_PHASE_MAP = {'feature freeze': '', |
| 'branch': '', |
| 'stable': 'stable', |
| 'stable-full': 'stable', |
| 'stable-exp': 'stable-exp',} |
| # We only care about Launch-M-<type>-<m>-Stable|Stable-Exp labels for OS. |
| OS_M_LABELS_RE = re.compile( |
| r'^Launch-M-(?P<type>Approved|Target)-(?P<m>\d\d)-' |
| r'(?P<channel>Stable$|Stable-Exp$)', re.IGNORECASE) |
| |
| CAN = 2 # Query for open issues only |
| # Ensure empty group_by_spec and sort_spec so issues are sorted by 'ID'. |
| GROUP_BY_SPEC = '' |
| SORT_SPEC = '' |
| |
| CONVERT_NUM = 20 |
| CONVERT_START = 0 |
| VERIFY_NUM = 400 |
| |
| # Queries |
| QUERY_MAP = { |
| 'default': |
| 'Type=Launch Rollout-Type=Default OS=Windows,Mac,Linux,Android,iOS', |
| 'finch': 'Type=Launch Rollout-Type=Finch OS=Windows,Mac,Linux,Android,iOS', |
| 'os': 'Type=Launch OS=Chrome -OS=Windows,Mac,Linux,Android,iOS' |
| ' Rollout-Type=Default', |
| 'os-finch': 'Type=Launch OS=Chrome -OS=Windows,Mac,Linux,Android,iOS' |
| ' Rollout-Type=Finch'} |
| |
| TEMPLATE_MAP = { |
| 'default': 'Chrome Launch - Default', |
| 'finch': 'Chrome Launch - Experimental', |
| 'os': 'Chrome OS Launch - Default', |
| 'os-finch': 'Chrome OS Launch - Experimental', |
| } |
| |
| ProjectInfo = collections.namedtuple( |
| 'ProjectInfo', 'config, q, approval_values, phases, ' |
| 'pm_fid, tl_fid, te_fid, ux_fid, m_target_id, m_approved_id, ' |
| 'phase_map, approvals_to_labels, labels_re') |
| |
| |
| # TODO: change to FlaskInternalTask when convert to flask |
| class FLTConvertTask(jsonfeed.InternalTask): |
| """FLTConvert converts current Type=Launch issues into Type=FLT-Launch.""" |
| |
| def AssertBasePermission(self, mr): |
| super(FLTConvertTask, self).AssertBasePermission(mr) |
| if not mr.auth.user_pb.is_site_admin: |
| raise permissions.PermissionException( |
| 'Only site admins may trigger conversion job') |
| |
| def UndoConversion(self, mr): |
| with work_env.WorkEnv(mr, self.services) as we: |
| pipeline = we.ListIssues( |
| 'Type=FLT-Launch FLT=Conversion', ['chromium'], mr.auth.user_id, |
| CONVERT_NUM, CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False) |
| |
| project = self.services.project.GetProjectByName(mr.cnxn, 'chromium') |
| config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id) |
| pm_id = tracker_bizobj.FindFieldDef('PM', config).field_id |
| tl_id = tracker_bizobj.FindFieldDef('TL', config).field_id |
| te_id = tracker_bizobj.FindFieldDef('TE', config).field_id |
| ux_id = tracker_bizobj.FindFieldDef('UX', config).field_id |
| for possible_stale_issue in pipeline.visible_results: |
| issue = self.services.issue.GetIssue( |
| mr.cnxn, possible_stale_issue.issue_id, use_cache=False) |
| |
| issue.approval_values = [] |
| issue.phases = [] |
| issue.field_values = [fv for fv in issue.field_values |
| if fv.phase_id is None] |
| issue.field_values = [fv for fv in issue.field_values |
| if fv.field_id not in |
| [pm_id, tl_id, te_id, ux_id]] |
| issue.labels.remove('Type-FLT-Launch') |
| issue.labels.remove('FLT-Conversion') |
| issue.labels.append('Type-Launch') |
| |
| self.services.issue._UpdateIssuesApprovals(mr.cnxn, issue) |
| self.services.issue.UpdateIssue(mr.cnxn, issue) |
| return {'deleting': [issue.local_id for issue in pipeline.visible_results], |
| 'num': len(pipeline.visible_results), |
| } |
| |
| def VerifyConversion(self, mr): |
| """Verify that all FLT-Conversion issues were converted correctly.""" |
| with work_env.WorkEnv(mr, self.services) as we: |
| pipeline = we.ListIssues( |
| 'FLT=Conversion', ['chromium'], mr.auth.user_id, VERIFY_NUM, |
| CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False) |
| |
| project = self.services.project.GetProjectByName(mr.cnxn, 'chromium') |
| config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id) |
| browser_approval_names = {fd.field_id: fd.field_name for fd |
| in config.field_defs if fd.field_name in |
| BROWSER_APPROVALS_TO_LABELS.keys()} |
| os_approval_names = {fd.field_id: fd.field_name for fd in config.field_defs |
| if (fd.field_name in OS_APPROVALS_TO_LABELS.keys()) |
| or fd.field_name == 'ChromeOS-Enterprise'} |
| pm_id = tracker_bizobj.FindFieldDef('PM', config).field_id |
| tl_id = tracker_bizobj.FindFieldDef('TL', config).field_id |
| te_id = tracker_bizobj.FindFieldDef('TE', config).field_id |
| ux_id = tracker_bizobj.FindFieldDef('UX', config).field_id |
| mapproved_id = tracker_bizobj.FindFieldDef('M-Approved', config).field_id |
| mtarget_id = tracker_bizobj.FindFieldDef('M-Target', config).field_id |
| |
| problems = [] |
| for possible_stale_issue in pipeline.allowed_results: |
| issue = self.services.issue.GetIssue( |
| mr.cnxn, possible_stale_issue.issue_id, use_cache=False) |
| # Check correct template used |
| approval_names = browser_approval_names |
| approvals_to_labels = BROWSER_APPROVALS_TO_LABELS |
| m_labels_re = BROWSER_M_LABELS_RE |
| label_channel_to_phase_id = { |
| phase.name.lower(): phase.phase_id for phase in issue.phases} |
| if [l for l in issue.labels if l.startswith('OS-')] == ['OS-Chrome']: |
| approval_names = os_approval_names |
| m_labels_re = OS_M_LABELS_RE |
| approvals_to_labels = OS_APPROVALS_TO_LABELS |
| # OS default launch |
| if 'Rollout-Type-Default' in issue.labels: |
| if not all(phase.name in ['Feature Freeze', 'Branch', 'Stable'] |
| for phase in issue.phases): |
| problems.append(( |
| issue.local_id, 'incorrect phases for OS default launch.')) |
| # OS finch launch |
| elif 'Rollout-Type-Finch' in issue.labels: |
| if not all(phase.name in ( |
| 'Feature Freeze', 'Branch', 'Stable-Exp', 'Stable-Full') |
| for phase in issue.phases): |
| problems.append(( |
| issue.local_id, 'incorrect phases for OS finch launch.')) |
| else: |
| problems.append(( |
| issue.local_id, |
| 'no rollout-type; should not have been converted')) |
| # Browser default launch |
| elif 'Rollout-Type-Default' in issue.labels: |
| if not all(phase.name.lower() in ['beta', 'stable'] |
| for phase in issue.phases): |
| problems.append(( |
| issue.local_id, 'incorrect phases for Default rollout')) |
| # Browser finch launch |
| elif 'Rollout-Type-Finch' in issue.labels: |
| if not all(phase.name.lower() in ['beta', 'stable-exp', 'stable-full'] |
| for phase in issue.phases): |
| problems.append(( |
| issue.local_id, 'incorrect phases for Finch rollout')) |
| else: |
| problems.append(( |
| issue.local_id, |
| 'no rollout-type; should not have been converted')) |
| |
| # Check approval_values |
| for av in issue.approval_values: |
| name = approval_names.get(av.approval_id) |
| if name == 'ChromeOS-Enterprise': |
| if av.status != tracker_pb2.ApprovalStatus.NEEDS_REVIEW: |
| problems.append((issue.local_id, 'bad ChromeOS-Enterprise status')) |
| continue |
| label_pre = approvals_to_labels.get(name) |
| if not label_pre: |
| # either name was None or not found in APPROVALS_TO_LABELS |
| problems.append((issue.local_id, 'approval %s not recognized' % name)) |
| continue |
| label_value = next((l[len(label_pre):] for l in issue.labels |
| if l.startswith(label_pre)), None) |
| if (not label_value or label_value == 'NotReviewed') and av.status in [ |
| tracker_pb2.ApprovalStatus.NOT_SET, |
| tracker_pb2.ApprovalStatus.NEEDS_REVIEW]: |
| continue |
| if av.status is VALUE_TO_STATUS.get(label_value): |
| continue |
| # neither of the above ifs passed |
| problems.append((issue.local_id, |
| 'approval %s has status %r for label value %s' % ( |
| name, av.status.name, label_value))) |
| |
| # Check people field_values |
| expected_people_fvs = self.ConvertPeopleLabels( |
| mr, issue.labels, pm_id, tl_id, te_id, ux_id) |
| for people_fv in expected_people_fvs: |
| if people_fv not in issue.field_values: |
| if people_fv.field_id == tl_id: |
| role = 'TL' |
| elif people_fv.field_id == pm_id: |
| role = 'PM' |
| elif people_fv.field_id == ux_id: |
| role = 'UX' |
| else: |
| role = 'TE' |
| problems.append((issue.local_id, 'missing a field for %s' % role)) |
| |
| # Check M phase field_values |
| for label in issue.labels: |
| match = re.match(m_labels_re, label) |
| if match: |
| channel = match.group('channel') |
| if (channel.lower() == 'stable-exp' |
| and 'Rollout-Type-Default' in issue.labels): |
| # ignore stable-exp for default rollouts. |
| continue |
| milestone = match.group('m') |
| m_type = match.group('type') |
| m_id = mapproved_id if m_type == 'Approved' else mtarget_id |
| phase_id = label_channel_to_phase_id.get( |
| channel.lower(), label_channel_to_phase_id.get('stable-full')) |
| if not next(( |
| fv for fv in issue.field_values |
| if fv.phase_id == phase_id and fv.field_id == m_id and |
| fv.int_value == int(milestone)), None): |
| problems.append(( |
| issue.local_id, 'no phase field for label %s' % label)) |
| |
| return { |
| 'problems found': ['issue %d: %s' % problem for problem in problems], |
| 'issues verified': ['issue %d' % issue.local_id for |
| issue in pipeline.allowed_results], |
| 'num': len(pipeline.allowed_results), |
| } |
| |
| def HandleRequest(self, mr): |
| """Convert Type=Launch issues to new Type=FLT-Launch issues.""" |
| launch = mr.GetParam('launch') |
| if launch == 'delete': |
| return self.UndoConversion(mr) |
| if launch == 'verify': |
| return self.VerifyConversion(mr) |
| project_info = self.FetchAndAssertProjectInfo(mr) |
| |
| # Search for issues: |
| with work_env.WorkEnv(mr, self.services) as we: |
| pipeline = we.ListIssues( |
| project_info.q, ['chromium'], mr.auth.user_id, CONVERT_NUM, |
| CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False) |
| |
| # Convert issues: |
| for possible_stale_issue in pipeline.visible_results: |
| # Note: These approval values and phases from templates will be used |
| # and modified to create approval values and phases for each issue. |
| # We need to create copies for each issue so changes are not carried |
| # over to the conversion of the next issue in the loop. |
| template_avs = self.CreateApprovalCopies(project_info.approval_values) |
| template_phases = self.CreatePhasesCopies(project_info.phases) |
| issue = self.services.issue.GetIssue( |
| mr.cnxn, possible_stale_issue.issue_id, use_cache=False) |
| new_approvals = ConvertLaunchLabels( |
| issue.labels, template_avs, |
| project_info.config.field_defs, project_info.approvals_to_labels) |
| m_fvs = ConvertMLabels( |
| issue.labels, template_phases, |
| project_info.m_target_id, project_info.m_approved_id, |
| project_info.labels_re, project_info.phase_map) |
| people_fvs = self.ConvertPeopleLabels( |
| mr, issue.labels, |
| project_info.pm_fid, project_info.tl_fid, project_info.te_fid, |
| project_info.ux_fid) |
| amendments = self.ExecuteIssueChanges( |
| project_info.config, issue, new_approvals, |
| template_phases, m_fvs + people_fvs) |
| logging.info(amendments) |
| |
| return { |
| 'converted_issues': [ |
| issue.local_id for issue in pipeline.visible_results], |
| 'num': len(pipeline.visible_results), |
| } |
| |
| def CreateApprovalCopies(self, avs): |
| return [ |
| tracker_pb2.ApprovalValue( |
| approval_id=av.approval_id, |
| status=av.status, |
| setter_id=av.setter_id, |
| set_on=av.set_on, |
| phase_id=av.phase_id) for av in avs |
| ] |
| |
| def CreatePhasesCopies(self, phases): |
| return [ |
| tracker_pb2.Phase( |
| phase_id=phase.phase_id, |
| name=phase.name, |
| rank=phase.rank) for phase in phases |
| ] |
| |
| def FetchAndAssertProjectInfo(self, mr): |
| # Get request details |
| launch = mr.GetParam('launch') |
| logging.info(launch) |
| q = QUERY_MAP.get(launch) |
| template_name = TEMPLATE_MAP.get(launch) |
| assert q and template_name, 'bad launch type: %s' % launch |
| |
| phase_map = ( |
| OS_PHASE_MAP if launch in ['os', 'os-finch'] else BROWSER_PHASE_MAP) |
| approvals_to_labels = ( |
| OS_APPROVALS_TO_LABELS if launch in ['os', 'os-finch'] |
| else BROWSER_APPROVALS_TO_LABELS) |
| m_labels_re = ( |
| OS_M_LABELS_RE if launch in ['os', 'os-finch'] else BROWSER_M_LABELS_RE) |
| |
| # Get project, config, template, assert template in project |
| project = self.services.project.GetProjectByName(mr.cnxn, 'chromium') |
| config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id) |
| template = self.services.template.GetTemplateByName( |
| mr.cnxn, template_name, project.project_id) |
| assert template, 'template %s not found in chromium project' % template_name |
| |
| # Get template approval_values/phases and assert they are expected |
| approval_values, phases = template_helpers.FilterApprovalsAndPhases( |
| template.approval_values, template.phases, config) |
| assert approval_values and phases, ( |
| 'no approvals or phases in %s' % template_name) |
| assert all(phase.name.lower() in list( |
| phase_map.keys()) for phase in phases), ( |
| 'one or more phases not recognized') |
| if launch in ['finch', 'os', 'os-finch']: |
| assert all( |
| av.status is tracker_pb2.ApprovalStatus.NEEDS_REVIEW |
| for av in approval_values |
| ), '%s template not set up correctly' % launch |
| |
| approval_fds = {fd.field_id: fd.field_name for fd in config.field_defs |
| if fd.field_type is tracker_pb2.FieldTypes.APPROVAL_TYPE} |
| assert all( |
| approval_fds.get(av.approval_id) in list(approvals_to_labels.keys()) |
| for av in approval_values |
| if approval_fds.get(av.approval_id) != 'ChromeOS-Enterprise'), ( |
| 'one or more approvals not recognized') |
| approval_def_ids = [ad.approval_id for ad in config.approval_defs] |
| assert all(av.approval_id in approval_def_ids for av in approval_values), ( |
| 'one or more approvals not in config.approval_defs') |
| |
| # Get relevant USER_TYPE FieldDef ids and assert they exist |
| user_fds = {fd.field_name.lower(): fd.field_id for fd in config.field_defs |
| if fd.field_type is tracker_pb2.FieldTypes.USER_TYPE} |
| logging.info('project USER_TYPE FieldDefs: %s' % user_fds) |
| pm_fid = user_fds.get(PM_FIELD) |
| assert pm_fid, 'project has no FieldDef %s' % PM_FIELD |
| tl_fid = user_fds.get(TL_FIELD) |
| assert tl_fid, 'project has no FieldDef %s' % TL_FIELD |
| te_fid = user_fds.get(TE_FIELD) |
| assert te_fid, 'project has no FieldDef %s' % TE_FIELD |
| ux_fid = user_fds.get(UX_FIELD) |
| assert ux_fid, 'project has no FieldDef %s' % UX_FIELD |
| |
| # Get relevant M Phase INT_TYPE FieldDef ids and assert they exist |
| phase_int_fds = {fd.field_name.lower(): fd.field_id |
| for fd in config.field_defs |
| if fd.field_type is tracker_pb2.FieldTypes.INT_TYPE |
| and fd.is_phase_field and fd.is_multivalued} |
| logging.info( |
| 'project Phase INT_TYPE multivalued FieldDefs: %s' % phase_int_fds) |
| m_target_id = phase_int_fds.get(MTARGET_FIELD) |
| assert m_target_id, 'project has no FieldDef %s' % MTARGET_FIELD |
| m_approved_id = phase_int_fds.get(MAPPROVED_FIELD) |
| assert m_approved_id, 'project has no FieldDef %s' % MAPPROVED_FIELD |
| |
| return ProjectInfo(config, q, approval_values, phases, pm_fid, tl_fid, |
| te_fid, ux_fid, m_target_id, m_approved_id, phase_map, |
| approvals_to_labels, m_labels_re) |
| |
| # TODO(jojwang): mr needs to be passed in as arg and |
| # all self.mr should be changed to mr |
| def ExecuteIssueChanges(self, config, issue, new_approvals, phases, new_fvs): |
| # Apply Approval and phase changes |
| approval_defs_by_id = {ad.approval_id: ad for ad in config.approval_defs} |
| for av in new_approvals: |
| ad = approval_defs_by_id.get(av.approval_id) |
| if ad: |
| av.approver_ids = ad.approver_ids |
| survey = '' |
| if ad.survey: |
| questions = ad.survey.split('\n') |
| survey = '\n'.join(['<b>' + q + '</b>' for q in questions]) |
| self.services.issue.InsertComment( |
| self.mr.cnxn, tracker_pb2.IssueComment( |
| issue_id=issue.issue_id, project_id=issue.project_id, |
| user_id=self.mr.auth.user_id, content=survey, |
| is_description=True, approval_id=av.approval_id, |
| timestamp=int(time.time()))) |
| else: |
| logging.info( |
| 'ERROR: ApprovalDef %r for ApprovalValue %r not valid', ad, av) |
| issue.approval_values = new_approvals |
| self.services.issue._UpdateIssuesApprovals(self.mr.cnxn, issue) |
| |
| # Apply field value changes |
| issue.phases = phases |
| delta = tracker_bizobj.MakeIssueDelta( |
| None, None, [], [], [], [], ['Type-FLT-Launch', 'FLT-Conversion'], |
| ['Type-Launch'], new_fvs, [], [], [], [], [], [], None, None) |
| amendments, _ = self.services.issue.DeltaUpdateIssue( |
| self.mr.cnxn, self.services, self.mr.auth.user_id, issue.project_id, |
| config, issue, delta, comment=CONVERSION_COMMENT) |
| |
| return amendments |
| |
| def ConvertPeopleLabels( |
| self, mr, labels, pm_field_id, tl_field_id, te_field_id, ux_field_id): |
| field_values = [] |
| pm_ldap, tl_ldap, test_ldaps, ux_ldaps = ExtractLabelLDAPs(labels) |
| |
| pm_fv = self.CreateUserFieldValue(mr, pm_ldap, pm_field_id) |
| if pm_fv: |
| field_values.append(pm_fv) |
| |
| tl_fv = self.CreateUserFieldValue(mr, tl_ldap, tl_field_id) |
| if tl_fv: |
| field_values.append(tl_fv) |
| |
| for test_ldap in test_ldaps: |
| te_fv = self.CreateUserFieldValue(mr, test_ldap, te_field_id) |
| if te_fv: |
| field_values.append(te_fv) |
| |
| for ux_ldap in ux_ldaps: |
| ux_fv = self.CreateUserFieldValue(mr, ux_ldap, ux_field_id) |
| if ux_fv: |
| field_values.append(ux_fv) |
| return field_values |
| |
| def CreateUserFieldValue(self, mr, ldap, field_id): |
| if ldap is None: |
| return None |
| try: |
| user_id = self.services.user.LookupUserID(mr.cnxn, ldap+'@chromium.org') |
| except exceptions.NoSuchUserException: |
| try: |
| user_id = self.services.user.LookupUserID(mr.cnxn, ldap+'@google.com') |
| except exceptions.NoSuchUserException: |
| logging.info('No chromium.org or google.com accound found for %s', ldap) |
| return None |
| return tracker_bizobj.MakeFieldValue( |
| field_id, None, None, user_id, None, None, False) |
| |
| # def GetFLTConvertTask(self, **kwargs): |
| # return self.handler(**kwargs) |
| |
| # def PostFLTConvertTask(self, **kwargs): |
| # return self.handler(**kwargs) |
| |
| |
| def ConvertMLabels( |
| labels, phases, m_target_id, m_approved_id, labels_re, phase_map): |
| field_values = [] |
| for label in labels: |
| match = re.match(labels_re, label) |
| if match: |
| milestone = match.group('m') |
| m_type = match.group('type') |
| channel = match.group('channel') |
| for phase in phases: |
| # We know get(phase) will return something because |
| # we're checking before ConvertMLabels, that all phases |
| # exist in BROWSER_PHASE_MAP or OS_PHASE_MAP |
| if phase_map.get(phase.name.lower()) == channel.lower(): |
| field_id = m_target_id if ( |
| m_type.lower() == 'target') else m_approved_id |
| field_values.append(tracker_bizobj.MakeFieldValue( |
| field_id, int(milestone), None, None, None, None, False, |
| phase_id=phase.phase_id)) |
| break # exit phase loop if match is found. |
| return field_values |
| |
| |
| def ConvertLaunchLabels(labels, approvals, project_fds, approvals_to_labels): |
| """Converts 'Launch-[Review]' values into statuses for given approvals.""" |
| label_values = {} |
| for label in labels: |
| launch_match = REVIEW_LABELS_RE.match(label) |
| if launch_match: |
| prefix = launch_match.group() |
| value = label[len(prefix):] # returns 'Yes' from 'Launch-UI-Yes' |
| label_values[prefix] = value |
| |
| field_names_dict = {fd.field_id: fd.field_name for fd in project_fds} |
| for approval in approvals: |
| approval_name = field_names_dict.get(approval.approval_id, '') |
| old_prefix = approvals_to_labels.get(approval_name) |
| label_value = label_values.get(old_prefix, '') |
| # if label_value not found in VALUE_TO_STATUS, use current status. |
| approval.status = VALUE_TO_STATUS.get(label_value, approval.status) |
| |
| return approvals |
| |
| |
| def ExtractLabelLDAPs(labels): |
| """Extracts LDAPs from labels 'PM-', 'TL-', 'UX-', and 'test-'""" |
| |
| pm_ldap = None |
| tl_ldap = None |
| test_ldaps = [] |
| ux_ldaps = [] |
| for label in labels: |
| label = label.lower() |
| if label.startswith(PM_PREFIX): |
| pm_ldap = label[len(PM_PREFIX):] |
| elif label.startswith(TL_PREFIX): |
| tl_ldap = label[len(TL_PREFIX):] |
| elif label.startswith(TEST_PREFIX): |
| ldap = label[len(TEST_PREFIX):] |
| if ldap: |
| test_ldaps.append(ldap) |
| elif label.startswith(UX_PREFIX): |
| ldap = label[len(UX_PREFIX):] |
| if ldap: |
| ux_ldaps.append(ldap) |
| return pm_ldap, tl_ldap, test_ldaps, ux_ldaps |