def testGetIssueComponentsAndAncestors_AffectsSomeComponents(self):
   config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
   cd = tracker_pb2.ComponentDef(component_id=1, path='UI')
   config.component_defs.append(cd)
   cd2 = tracker_pb2.ComponentDef(component_id=2, path='UI>Splash')
   config.component_defs.append(cd2)
   issue = tracker_pb2.Issue(component_ids=[2])
   actual = tracker_bizobj.GetIssueComponentsAndAncestors(issue, config)
   self.assertEqual([cd, cd2], actual)
Esempio n. 2
0
def _ComputeDerivedFields(cnxn, services, issue, config, rules, predicate_asts):
  """Compute derived field values for an issue based on filter rules.

  Args:
    cnxn: database connection, used to look up user IDs.
    services: persistence layer for users, issues, and projects.
    issue: the issue to examine.
    config: ProjectIssueConfig for the project containing the issue.
    rules: list of FilterRule PBs.
    predicate_asts: QueryAST PB for each rule.

  Returns:
    A 8-tuple of derived values for owner_id, status, cc_ids, labels,
    notify_addrs, traces, warnings, and errors.  These values are the result
    of applying all rules in order.  Filter rules only produce derived values
    that do not conflict with the explicit field values of the issue.
  """
  excl_prefixes = [
      prefix.lower() for prefix in config.exclusive_label_prefixes]
  # Examine the explicit labels and Cc's on the issue.
  lower_labels = [lab.lower() for lab in issue.labels]
  label_set = set(lower_labels)
  cc_set = set(issue.cc_ids)
  excl_prefixes_used = set()
  for lab in lower_labels:
    prefix = lab.split('-')[0]
    if prefix in excl_prefixes:
      excl_prefixes_used.add(prefix)
  prefix_values_added = {}

  # Start with the assumption that rules don't change anything, then
  # accumulate changes.
  derived_owner_id = framework_constants.NO_USER_SPECIFIED
  derived_status = ''
  derived_cc_ids = []
  derived_labels = []
  derived_notify_addrs = []
  traces = {}  # {(field_id, new_value): explanation_str}
  new_warnings = []
  new_errors = []

  def AddLabelConsideringExclusivePrefixes(label):
    lab_lower = label.lower()
    if lab_lower in label_set:
      return False  # We already have that label.
    prefix = lab_lower.split('-')[0]
    if '-' in lab_lower and prefix in excl_prefixes:
      if prefix in excl_prefixes_used:
        return False  # Issue already has that prefix.
      # Replace any earlied-added label that had the same exclusive prefix.
      if prefix in prefix_values_added:
        label_set.remove(prefix_values_added[prefix].lower())
        derived_labels.remove(prefix_values_added[prefix])
      prefix_values_added[prefix] = label

    derived_labels.append(label)
    label_set.add(lab_lower)
    return True

  # Apply component labels and auto-cc's before doing the rules.
  components = tracker_bizobj.GetIssueComponentsAndAncestors(issue, config)
  for cd in components:
    for cc_id in cd.cc_ids:
      if cc_id not in cc_set:
        derived_cc_ids.append(cc_id)
        cc_set.add(cc_id)
        traces[(tracker_pb2.FieldID.CC, cc_id)] = (
            'Added by component %s' % cd.path)

    for label_id in cd.label_ids:
      lab = services.config.LookupLabel(cnxn, config.project_id, label_id)
      if AddLabelConsideringExclusivePrefixes(lab):
        traces[(tracker_pb2.FieldID.LABELS, lab)] = (
            'Added by component %s' % cd.path)

  # Apply each rule in order. Later rules see the results of earlier rules.
  # Later rules can overwrite or add to results of earlier rules.
  # TODO(jrobbins): also pass in in-progress values for owner and CCs so
  # that early rules that set those can affect later rules that check them.
  for rule, predicate_ast in zip(rules, predicate_asts):
    (rule_owner_id, rule_status, rule_add_cc_ids,
     rule_add_labels, rule_add_notify, rule_add_warning,
     rule_add_error) = _ApplyRule(
         cnxn, services, rule, predicate_ast, issue, label_set, config)

    # logging.info(
    #    'rule "%s" gave %r, %r, %r, %r, %r',
    #     rule.predicate, rule_owner_id, rule_status, rule_add_cc_ids,
    #     rule_add_labels, rule_add_notify)

    if rule_owner_id and not issue.owner_id:
      derived_owner_id = rule_owner_id
      traces[(tracker_pb2.FieldID.OWNER, rule_owner_id)] = (
        'Added by rule: IF %s THEN SET DEFAULT OWNER' % rule.predicate)

    if rule_status and not issue.status:
      derived_status = rule_status
      traces[(tracker_pb2.FieldID.STATUS, rule_status)] = (
        'Added by rule: IF %s THEN SET DEFAULT STATUS' % rule.predicate)

    for cc_id in rule_add_cc_ids:
      if cc_id not in cc_set:
        derived_cc_ids.append(cc_id)
        cc_set.add(cc_id)
        traces[(tracker_pb2.FieldID.CC, cc_id)] = (
          'Added by rule: IF %s THEN ADD CC' % rule.predicate)

    for lab in rule_add_labels:
      if AddLabelConsideringExclusivePrefixes(lab):
        traces[(tracker_pb2.FieldID.LABELS, lab)] = (
            'Added by rule: IF %s THEN ADD LABEL' % rule.predicate)

    for addr in rule_add_notify:
      if addr not in derived_notify_addrs:
        derived_notify_addrs.append(addr)
        # Note: No trace because also-notify addresses are not shown in the UI.

    if rule_add_warning:
      new_warnings.append(rule_add_warning)
      traces[(tracker_pb2.FieldID.WARNING, rule_add_warning)] = (
        'Added by rule: IF %s THEN ADD WARNING' % rule.predicate)

    if rule_add_error:
      new_errors.append(rule_add_error)
      traces[(tracker_pb2.FieldID.ERROR, rule_add_error)] = (
        'Added by rule: IF %s THEN ADD ERROR' % rule.predicate)

  return (derived_owner_id, derived_status, derived_cc_ids, derived_labels,
          derived_notify_addrs, traces, new_warnings, new_errors)