blob: a88f841b651d7ab188f30ed3e6442b340314e553 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2018 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""FLT task to be manually triggered to convert launch issues."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import collections
11import logging
12import re
13import settings
14import time
15
16from businesslogic import work_env
17from framework import permissions
18from framework import exceptions
19from framework import jsonfeed
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010020from mrproto import tracker_pb2
Copybara854996b2021-09-07 19:36:02 +000021from tracker import template_helpers
22from tracker import tracker_bizobj
23
24PM_PREFIX = 'pm-'
25TL_PREFIX = 'tl-'
26TEST_PREFIX = 'test-'
27UX_PREFIX = 'ux-'
28
29PM_FIELD = 'pm'
30TL_FIELD = 'tl'
31TE_FIELD = 'te'
32UX_FIELD = 'ux'
33MTARGET_FIELD = 'm-target'
34MAPPROVED_FIELD = 'm-approved'
35
36CONVERSION_COMMENT = 'Automatic generating of FLT Launch data.'
37
38BROWSER_APPROVALS_TO_LABELS = {
39 'Chrome-Accessibility': 'Launch-Accessibility-',
40 'Chrome-Leadership-Exp': 'Launch-Exp-Leadership-',
41 'Chrome-Leadership-Full': 'Launch-Leadership-',
42 'Chrome-Legal': 'Launch-Legal-',
43 'Chrome-Privacy': 'Launch-Privacy-',
44 'Chrome-Security': 'Launch-Security-',
45 'Chrome-Test': 'Launch-Test-',
46 'Chrome-UX': 'Launch-UI-',
47 }
48
49OS_APPROVALS_TO_LABELS = {
50 'ChromeOS-Accessibility': 'Launch-Accessibility-',
51 'ChromeOS-Leadership-Exp': 'Launch-Exp-Leadership-',
52 'ChromeOS-Leadership-Full': 'Launch-Leadership-',
53 'ChromeOS-Legal': 'Launch-Legal-',
54 'ChromeOS-Privacy': 'Launch-Privacy-',
55 'ChromeOS-Security': 'Launch-Security-',
56 'ChromeOS-Test': 'Launch-Test-',
57 'ChromeOS-UX': 'Launch-UI-',
58 }
59
60# 'NotReviewed' not included because this should be converted to
61# the template approval's default value, eg NOT_SET OR NEEDS_REVIEW
62VALUE_TO_STATUS = {
63 'ReviewRequested': tracker_pb2.ApprovalStatus.REVIEW_REQUESTED,
64 'NeedInfo': tracker_pb2.ApprovalStatus.NEED_INFO,
65 'Yes': tracker_pb2.ApprovalStatus.APPROVED,
66 'No': tracker_pb2.ApprovalStatus.NOT_APPROVED,
67 'NA': tracker_pb2.ApprovalStatus.NA,
68 # 'Started' is not a valid label value in the chromium project,
69 # but for some reason, some labels have this value.
70 'Started': tracker_pb2.ApprovalStatus.REVIEW_STARTED,
71}
72
73# This works in the Browser and OS process because
74# BROWSER_APPROVALS_TO_LABELS and OS_APPROVALS_TO_LABELS have the same values.
75# Adding '^' before each label prefix to ensure Blah-Launch-UI-Yes is ignored
76REVIEW_LABELS_RE = re.compile('^' + '|^'.join(
77 list(OS_APPROVALS_TO_LABELS.values())))
78
79# Maps template phases to channel names in 'Launch-M-Target-80-[Channel]' labels
80BROWSER_PHASE_MAP = {
81 'beta': 'beta',
82 'stable': 'stable',
83 'stable-full': 'stable',
84 'stable-exp': 'stable-exp',
85 }
86
87PHASE_PAT = '$|'.join(list(BROWSER_PHASE_MAP.values()))
88# Matches launch milestone labels, eg. Launch-M-Target-70-Stable-Exp
89BROWSER_M_LABELS_RE = re.compile(
90 r'^Launch-M-(?P<type>Approved|Target)-(?P<m>\d\d)-'
91 r'(?P<channel>%s$)' % PHASE_PAT,
92 re.IGNORECASE)
93
94OS_PHASE_MAP = {'feature freeze': '',
95 'branch': '',
96 'stable': 'stable',
97 'stable-full': 'stable',
98 'stable-exp': 'stable-exp',}
99# We only care about Launch-M-<type>-<m>-Stable|Stable-Exp labels for OS.
100OS_M_LABELS_RE = re.compile(
101 r'^Launch-M-(?P<type>Approved|Target)-(?P<m>\d\d)-'
102 r'(?P<channel>Stable$|Stable-Exp$)', re.IGNORECASE)
103
104CAN = 2 # Query for open issues only
105# Ensure empty group_by_spec and sort_spec so issues are sorted by 'ID'.
106GROUP_BY_SPEC = ''
107SORT_SPEC = ''
108
109CONVERT_NUM = 20
110CONVERT_START = 0
111VERIFY_NUM = 400
112
113# Queries
114QUERY_MAP = {
115 'default':
116 'Type=Launch Rollout-Type=Default OS=Windows,Mac,Linux,Android,iOS',
117 'finch': 'Type=Launch Rollout-Type=Finch OS=Windows,Mac,Linux,Android,iOS',
118 'os': 'Type=Launch OS=Chrome -OS=Windows,Mac,Linux,Android,iOS'
119 ' Rollout-Type=Default',
120 'os-finch': 'Type=Launch OS=Chrome -OS=Windows,Mac,Linux,Android,iOS'
121 ' Rollout-Type=Finch'}
122
123TEMPLATE_MAP = {
124 'default': 'Chrome Launch - Default',
125 'finch': 'Chrome Launch - Experimental',
126 'os': 'Chrome OS Launch - Default',
127 'os-finch': 'Chrome OS Launch - Experimental',
128}
129
130ProjectInfo = collections.namedtuple(
131 'ProjectInfo', 'config, q, approval_values, phases, '
132 'pm_fid, tl_fid, te_fid, ux_fid, m_target_id, m_approved_id, '
133 'phase_map, approvals_to_labels, labels_re')
134
135
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100136class FLTConvertTask(jsonfeed.InternalTask):
Copybara854996b2021-09-07 19:36:02 +0000137 """FLTConvert converts current Type=Launch issues into Type=FLT-Launch."""
138
139 def AssertBasePermission(self, mr):
140 super(FLTConvertTask, self).AssertBasePermission(mr)
141 if not mr.auth.user_pb.is_site_admin:
142 raise permissions.PermissionException(
143 'Only site admins may trigger conversion job')
144
145 def UndoConversion(self, mr):
146 with work_env.WorkEnv(mr, self.services) as we:
147 pipeline = we.ListIssues(
148 'Type=FLT-Launch FLT=Conversion', ['chromium'], mr.auth.user_id,
149 CONVERT_NUM, CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False)
150
151 project = self.services.project.GetProjectByName(mr.cnxn, 'chromium')
152 config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id)
153 pm_id = tracker_bizobj.FindFieldDef('PM', config).field_id
154 tl_id = tracker_bizobj.FindFieldDef('TL', config).field_id
155 te_id = tracker_bizobj.FindFieldDef('TE', config).field_id
156 ux_id = tracker_bizobj.FindFieldDef('UX', config).field_id
157 for possible_stale_issue in pipeline.visible_results:
158 issue = self.services.issue.GetIssue(
159 mr.cnxn, possible_stale_issue.issue_id, use_cache=False)
160
161 issue.approval_values = []
162 issue.phases = []
163 issue.field_values = [fv for fv in issue.field_values
164 if fv.phase_id is None]
165 issue.field_values = [fv for fv in issue.field_values
166 if fv.field_id not in
167 [pm_id, tl_id, te_id, ux_id]]
168 issue.labels.remove('Type-FLT-Launch')
169 issue.labels.remove('FLT-Conversion')
170 issue.labels.append('Type-Launch')
171
172 self.services.issue._UpdateIssuesApprovals(mr.cnxn, issue)
173 self.services.issue.UpdateIssue(mr.cnxn, issue)
174 return {'deleting': [issue.local_id for issue in pipeline.visible_results],
175 'num': len(pipeline.visible_results),
176 }
177
178 def VerifyConversion(self, mr):
179 """Verify that all FLT-Conversion issues were converted correctly."""
180 with work_env.WorkEnv(mr, self.services) as we:
181 pipeline = we.ListIssues(
182 'FLT=Conversion', ['chromium'], mr.auth.user_id, VERIFY_NUM,
183 CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False)
184
185 project = self.services.project.GetProjectByName(mr.cnxn, 'chromium')
186 config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id)
187 browser_approval_names = {fd.field_id: fd.field_name for fd
188 in config.field_defs if fd.field_name in
189 BROWSER_APPROVALS_TO_LABELS.keys()}
190 os_approval_names = {fd.field_id: fd.field_name for fd in config.field_defs
191 if (fd.field_name in OS_APPROVALS_TO_LABELS.keys())
192 or fd.field_name == 'ChromeOS-Enterprise'}
193 pm_id = tracker_bizobj.FindFieldDef('PM', config).field_id
194 tl_id = tracker_bizobj.FindFieldDef('TL', config).field_id
195 te_id = tracker_bizobj.FindFieldDef('TE', config).field_id
196 ux_id = tracker_bizobj.FindFieldDef('UX', config).field_id
197 mapproved_id = tracker_bizobj.FindFieldDef('M-Approved', config).field_id
198 mtarget_id = tracker_bizobj.FindFieldDef('M-Target', config).field_id
199
200 problems = []
201 for possible_stale_issue in pipeline.allowed_results:
202 issue = self.services.issue.GetIssue(
203 mr.cnxn, possible_stale_issue.issue_id, use_cache=False)
204 # Check correct template used
205 approval_names = browser_approval_names
206 approvals_to_labels = BROWSER_APPROVALS_TO_LABELS
207 m_labels_re = BROWSER_M_LABELS_RE
208 label_channel_to_phase_id = {
209 phase.name.lower(): phase.phase_id for phase in issue.phases}
210 if [l for l in issue.labels if l.startswith('OS-')] == ['OS-Chrome']:
211 approval_names = os_approval_names
212 m_labels_re = OS_M_LABELS_RE
213 approvals_to_labels = OS_APPROVALS_TO_LABELS
214 # OS default launch
215 if 'Rollout-Type-Default' in issue.labels:
216 if not all(phase.name in ['Feature Freeze', 'Branch', 'Stable']
217 for phase in issue.phases):
218 problems.append((
219 issue.local_id, 'incorrect phases for OS default launch.'))
220 # OS finch launch
221 elif 'Rollout-Type-Finch' in issue.labels:
222 if not all(phase.name in (
223 'Feature Freeze', 'Branch', 'Stable-Exp', 'Stable-Full')
224 for phase in issue.phases):
225 problems.append((
226 issue.local_id, 'incorrect phases for OS finch launch.'))
227 else:
228 problems.append((
229 issue.local_id,
230 'no rollout-type; should not have been converted'))
231 # Browser default launch
232 elif 'Rollout-Type-Default' in issue.labels:
233 if not all(phase.name.lower() in ['beta', 'stable']
234 for phase in issue.phases):
235 problems.append((
236 issue.local_id, 'incorrect phases for Default rollout'))
237 # Browser finch launch
238 elif 'Rollout-Type-Finch' in issue.labels:
239 if not all(phase.name.lower() in ['beta', 'stable-exp', 'stable-full']
240 for phase in issue.phases):
241 problems.append((
242 issue.local_id, 'incorrect phases for Finch rollout'))
243 else:
244 problems.append((
245 issue.local_id,
246 'no rollout-type; should not have been converted'))
247
248 # Check approval_values
249 for av in issue.approval_values:
250 name = approval_names.get(av.approval_id)
251 if name == 'ChromeOS-Enterprise':
252 if av.status != tracker_pb2.ApprovalStatus.NEEDS_REVIEW:
253 problems.append((issue.local_id, 'bad ChromeOS-Enterprise status'))
254 continue
255 label_pre = approvals_to_labels.get(name)
256 if not label_pre:
257 # either name was None or not found in APPROVALS_TO_LABELS
258 problems.append((issue.local_id, 'approval %s not recognized' % name))
259 continue
260 label_value = next((l[len(label_pre):] for l in issue.labels
261 if l.startswith(label_pre)), None)
262 if (not label_value or label_value == 'NotReviewed') and av.status in [
263 tracker_pb2.ApprovalStatus.NOT_SET,
264 tracker_pb2.ApprovalStatus.NEEDS_REVIEW]:
265 continue
266 if av.status is VALUE_TO_STATUS.get(label_value):
267 continue
268 # neither of the above ifs passed
269 problems.append((issue.local_id,
270 'approval %s has status %r for label value %s' % (
271 name, av.status.name, label_value)))
272
273 # Check people field_values
274 expected_people_fvs = self.ConvertPeopleLabels(
275 mr, issue.labels, pm_id, tl_id, te_id, ux_id)
276 for people_fv in expected_people_fvs:
277 if people_fv not in issue.field_values:
278 if people_fv.field_id == tl_id:
279 role = 'TL'
280 elif people_fv.field_id == pm_id:
281 role = 'PM'
282 elif people_fv.field_id == ux_id:
283 role = 'UX'
284 else:
285 role = 'TE'
286 problems.append((issue.local_id, 'missing a field for %s' % role))
287
288 # Check M phase field_values
289 for label in issue.labels:
290 match = re.match(m_labels_re, label)
291 if match:
292 channel = match.group('channel')
293 if (channel.lower() == 'stable-exp'
294 and 'Rollout-Type-Default' in issue.labels):
295 # ignore stable-exp for default rollouts.
296 continue
297 milestone = match.group('m')
298 m_type = match.group('type')
299 m_id = mapproved_id if m_type == 'Approved' else mtarget_id
300 phase_id = label_channel_to_phase_id.get(
301 channel.lower(), label_channel_to_phase_id.get('stable-full'))
302 if not next((
303 fv for fv in issue.field_values
304 if fv.phase_id == phase_id and fv.field_id == m_id and
305 fv.int_value == int(milestone)), None):
306 problems.append((
307 issue.local_id, 'no phase field for label %s' % label))
308
309 return {
310 'problems found': ['issue %d: %s' % problem for problem in problems],
311 'issues verified': ['issue %d' % issue.local_id for
312 issue in pipeline.allowed_results],
313 'num': len(pipeline.allowed_results),
314 }
315
316 def HandleRequest(self, mr):
317 """Convert Type=Launch issues to new Type=FLT-Launch issues."""
318 launch = mr.GetParam('launch')
319 if launch == 'delete':
320 return self.UndoConversion(mr)
321 if launch == 'verify':
322 return self.VerifyConversion(mr)
323 project_info = self.FetchAndAssertProjectInfo(mr)
324
325 # Search for issues:
326 with work_env.WorkEnv(mr, self.services) as we:
327 pipeline = we.ListIssues(
328 project_info.q, ['chromium'], mr.auth.user_id, CONVERT_NUM,
329 CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False)
330
331 # Convert issues:
332 for possible_stale_issue in pipeline.visible_results:
333 # Note: These approval values and phases from templates will be used
334 # and modified to create approval values and phases for each issue.
335 # We need to create copies for each issue so changes are not carried
336 # over to the conversion of the next issue in the loop.
337 template_avs = self.CreateApprovalCopies(project_info.approval_values)
338 template_phases = self.CreatePhasesCopies(project_info.phases)
339 issue = self.services.issue.GetIssue(
340 mr.cnxn, possible_stale_issue.issue_id, use_cache=False)
341 new_approvals = ConvertLaunchLabels(
342 issue.labels, template_avs,
343 project_info.config.field_defs, project_info.approvals_to_labels)
344 m_fvs = ConvertMLabels(
345 issue.labels, template_phases,
346 project_info.m_target_id, project_info.m_approved_id,
347 project_info.labels_re, project_info.phase_map)
348 people_fvs = self.ConvertPeopleLabels(
349 mr, issue.labels,
350 project_info.pm_fid, project_info.tl_fid, project_info.te_fid,
351 project_info.ux_fid)
352 amendments = self.ExecuteIssueChanges(
353 project_info.config, issue, new_approvals,
354 template_phases, m_fvs + people_fvs)
355 logging.info(amendments)
356
357 return {
358 'converted_issues': [
359 issue.local_id for issue in pipeline.visible_results],
360 'num': len(pipeline.visible_results),
361 }
362
363 def CreateApprovalCopies(self, avs):
364 return [
365 tracker_pb2.ApprovalValue(
366 approval_id=av.approval_id,
367 status=av.status,
368 setter_id=av.setter_id,
369 set_on=av.set_on,
370 phase_id=av.phase_id) for av in avs
371 ]
372
373 def CreatePhasesCopies(self, phases):
374 return [
375 tracker_pb2.Phase(
376 phase_id=phase.phase_id,
377 name=phase.name,
378 rank=phase.rank) for phase in phases
379 ]
380
381 def FetchAndAssertProjectInfo(self, mr):
382 # Get request details
383 launch = mr.GetParam('launch')
384 logging.info(launch)
385 q = QUERY_MAP.get(launch)
386 template_name = TEMPLATE_MAP.get(launch)
387 assert q and template_name, 'bad launch type: %s' % launch
388
389 phase_map = (
390 OS_PHASE_MAP if launch in ['os', 'os-finch'] else BROWSER_PHASE_MAP)
391 approvals_to_labels = (
392 OS_APPROVALS_TO_LABELS if launch in ['os', 'os-finch']
393 else BROWSER_APPROVALS_TO_LABELS)
394 m_labels_re = (
395 OS_M_LABELS_RE if launch in ['os', 'os-finch'] else BROWSER_M_LABELS_RE)
396
397 # Get project, config, template, assert template in project
398 project = self.services.project.GetProjectByName(mr.cnxn, 'chromium')
399 config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id)
400 template = self.services.template.GetTemplateByName(
401 mr.cnxn, template_name, project.project_id)
402 assert template, 'template %s not found in chromium project' % template_name
403
404 # Get template approval_values/phases and assert they are expected
405 approval_values, phases = template_helpers.FilterApprovalsAndPhases(
406 template.approval_values, template.phases, config)
407 assert approval_values and phases, (
408 'no approvals or phases in %s' % template_name)
409 assert all(phase.name.lower() in list(
410 phase_map.keys()) for phase in phases), (
411 'one or more phases not recognized')
412 if launch in ['finch', 'os', 'os-finch']:
413 assert all(
414 av.status is tracker_pb2.ApprovalStatus.NEEDS_REVIEW
415 for av in approval_values
416 ), '%s template not set up correctly' % launch
417
418 approval_fds = {fd.field_id: fd.field_name for fd in config.field_defs
419 if fd.field_type is tracker_pb2.FieldTypes.APPROVAL_TYPE}
420 assert all(
421 approval_fds.get(av.approval_id) in list(approvals_to_labels.keys())
422 for av in approval_values
423 if approval_fds.get(av.approval_id) != 'ChromeOS-Enterprise'), (
424 'one or more approvals not recognized')
425 approval_def_ids = [ad.approval_id for ad in config.approval_defs]
426 assert all(av.approval_id in approval_def_ids for av in approval_values), (
427 'one or more approvals not in config.approval_defs')
428
429 # Get relevant USER_TYPE FieldDef ids and assert they exist
430 user_fds = {fd.field_name.lower(): fd.field_id for fd in config.field_defs
431 if fd.field_type is tracker_pb2.FieldTypes.USER_TYPE}
432 logging.info('project USER_TYPE FieldDefs: %s' % user_fds)
433 pm_fid = user_fds.get(PM_FIELD)
434 assert pm_fid, 'project has no FieldDef %s' % PM_FIELD
435 tl_fid = user_fds.get(TL_FIELD)
436 assert tl_fid, 'project has no FieldDef %s' % TL_FIELD
437 te_fid = user_fds.get(TE_FIELD)
438 assert te_fid, 'project has no FieldDef %s' % TE_FIELD
439 ux_fid = user_fds.get(UX_FIELD)
440 assert ux_fid, 'project has no FieldDef %s' % UX_FIELD
441
442 # Get relevant M Phase INT_TYPE FieldDef ids and assert they exist
443 phase_int_fds = {fd.field_name.lower(): fd.field_id
444 for fd in config.field_defs
445 if fd.field_type is tracker_pb2.FieldTypes.INT_TYPE
446 and fd.is_phase_field and fd.is_multivalued}
447 logging.info(
448 'project Phase INT_TYPE multivalued FieldDefs: %s' % phase_int_fds)
449 m_target_id = phase_int_fds.get(MTARGET_FIELD)
450 assert m_target_id, 'project has no FieldDef %s' % MTARGET_FIELD
451 m_approved_id = phase_int_fds.get(MAPPROVED_FIELD)
452 assert m_approved_id, 'project has no FieldDef %s' % MAPPROVED_FIELD
453
454 return ProjectInfo(config, q, approval_values, phases, pm_fid, tl_fid,
455 te_fid, ux_fid, m_target_id, m_approved_id, phase_map,
456 approvals_to_labels, m_labels_re)
457
458 # TODO(jojwang): mr needs to be passed in as arg and
459 # all self.mr should be changed to mr
460 def ExecuteIssueChanges(self, config, issue, new_approvals, phases, new_fvs):
461 # Apply Approval and phase changes
462 approval_defs_by_id = {ad.approval_id: ad for ad in config.approval_defs}
463 for av in new_approvals:
464 ad = approval_defs_by_id.get(av.approval_id)
465 if ad:
466 av.approver_ids = ad.approver_ids
467 survey = ''
468 if ad.survey:
469 questions = ad.survey.split('\n')
470 survey = '\n'.join(['<b>' + q + '</b>' for q in questions])
471 self.services.issue.InsertComment(
472 self.mr.cnxn, tracker_pb2.IssueComment(
473 issue_id=issue.issue_id, project_id=issue.project_id,
474 user_id=self.mr.auth.user_id, content=survey,
475 is_description=True, approval_id=av.approval_id,
476 timestamp=int(time.time())))
477 else:
478 logging.info(
479 'ERROR: ApprovalDef %r for ApprovalValue %r not valid', ad, av)
480 issue.approval_values = new_approvals
481 self.services.issue._UpdateIssuesApprovals(self.mr.cnxn, issue)
482
483 # Apply field value changes
484 issue.phases = phases
485 delta = tracker_bizobj.MakeIssueDelta(
486 None, None, [], [], [], [], ['Type-FLT-Launch', 'FLT-Conversion'],
487 ['Type-Launch'], new_fvs, [], [], [], [], [], [], None, None)
488 amendments, _ = self.services.issue.DeltaUpdateIssue(
489 self.mr.cnxn, self.services, self.mr.auth.user_id, issue.project_id,
490 config, issue, delta, comment=CONVERSION_COMMENT)
491
492 return amendments
493
494 def ConvertPeopleLabels(
495 self, mr, labels, pm_field_id, tl_field_id, te_field_id, ux_field_id):
496 field_values = []
497 pm_ldap, tl_ldap, test_ldaps, ux_ldaps = ExtractLabelLDAPs(labels)
498
499 pm_fv = self.CreateUserFieldValue(mr, pm_ldap, pm_field_id)
500 if pm_fv:
501 field_values.append(pm_fv)
502
503 tl_fv = self.CreateUserFieldValue(mr, tl_ldap, tl_field_id)
504 if tl_fv:
505 field_values.append(tl_fv)
506
507 for test_ldap in test_ldaps:
508 te_fv = self.CreateUserFieldValue(mr, test_ldap, te_field_id)
509 if te_fv:
510 field_values.append(te_fv)
511
512 for ux_ldap in ux_ldaps:
513 ux_fv = self.CreateUserFieldValue(mr, ux_ldap, ux_field_id)
514 if ux_fv:
515 field_values.append(ux_fv)
516 return field_values
517
518 def CreateUserFieldValue(self, mr, ldap, field_id):
519 if ldap is None:
520 return None
521 try:
522 user_id = self.services.user.LookupUserID(mr.cnxn, ldap+'@chromium.org')
523 except exceptions.NoSuchUserException:
524 try:
525 user_id = self.services.user.LookupUserID(mr.cnxn, ldap+'@google.com')
526 except exceptions.NoSuchUserException:
527 logging.info('No chromium.org or google.com accound found for %s', ldap)
528 return None
529 return tracker_bizobj.MakeFieldValue(
530 field_id, None, None, user_id, None, None, False)
531
Adrià Vilanova Martínez9f9ade52022-10-10 23:20:11 +0200532 def PostFLTConvertTask(self, **kwargs):
533 return self.handler(**kwargs)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200534
Copybara854996b2021-09-07 19:36:02 +0000535
536def ConvertMLabels(
537 labels, phases, m_target_id, m_approved_id, labels_re, phase_map):
538 field_values = []
539 for label in labels:
540 match = re.match(labels_re, label)
541 if match:
542 milestone = match.group('m')
543 m_type = match.group('type')
544 channel = match.group('channel')
545 for phase in phases:
546 # We know get(phase) will return something because
547 # we're checking before ConvertMLabels, that all phases
548 # exist in BROWSER_PHASE_MAP or OS_PHASE_MAP
549 if phase_map.get(phase.name.lower()) == channel.lower():
550 field_id = m_target_id if (
551 m_type.lower() == 'target') else m_approved_id
552 field_values.append(tracker_bizobj.MakeFieldValue(
553 field_id, int(milestone), None, None, None, None, False,
554 phase_id=phase.phase_id))
555 break # exit phase loop if match is found.
556 return field_values
557
558
559def ConvertLaunchLabels(labels, approvals, project_fds, approvals_to_labels):
560 """Converts 'Launch-[Review]' values into statuses for given approvals."""
561 label_values = {}
562 for label in labels:
563 launch_match = REVIEW_LABELS_RE.match(label)
564 if launch_match:
565 prefix = launch_match.group()
566 value = label[len(prefix):] # returns 'Yes' from 'Launch-UI-Yes'
567 label_values[prefix] = value
568
569 field_names_dict = {fd.field_id: fd.field_name for fd in project_fds}
570 for approval in approvals:
571 approval_name = field_names_dict.get(approval.approval_id, '')
572 old_prefix = approvals_to_labels.get(approval_name)
573 label_value = label_values.get(old_prefix, '')
574 # if label_value not found in VALUE_TO_STATUS, use current status.
575 approval.status = VALUE_TO_STATUS.get(label_value, approval.status)
576
577 return approvals
578
579
580def ExtractLabelLDAPs(labels):
581 """Extracts LDAPs from labels 'PM-', 'TL-', 'UX-', and 'test-'"""
582
583 pm_ldap = None
584 tl_ldap = None
585 test_ldaps = []
586 ux_ldaps = []
587 for label in labels:
588 label = label.lower()
589 if label.startswith(PM_PREFIX):
590 pm_ldap = label[len(PM_PREFIX):]
591 elif label.startswith(TL_PREFIX):
592 tl_ldap = label[len(TL_PREFIX):]
593 elif label.startswith(TEST_PREFIX):
594 ldap = label[len(TEST_PREFIX):]
595 if ldap:
596 test_ldaps.append(ldap)
597 elif label.startswith(UX_PREFIX):
598 ldap = label[len(UX_PREFIX):]
599 if ldap:
600 ux_ldaps.append(ldap)
601 return pm_ldap, tl_ldap, test_ldaps, ux_ldaps