def _calculate_bugtask_condition(query_arguments): """Return a condition matching importance and status for the bugtasks. :param query_arguments: an iterable of (bugtask, target) pairs, as returned by get_structural_subscription_targets. """ # This handles importance and status, which are per bugtask. # The `query_arguments` collection has pairs of bugtask, target, # where one bugtask may repeat, to be paired with multiple targets. # We know it will not be empty, because we already escaped early in # _get_structural_subscription_filter_id_query with "if not # query_arguments: return None". # This approach groups the queries around shared statuses and importances # in order to address concerns like those raised in bug 731009. # We begin be establishing that grouping. The `statuses` dict maps # bugtask statuses to a mapping of bugtask importances to a list of # targets. More concisely: # statuses map -> importances map -> list of targets statuses = defaultdict(lambda: defaultdict(list)) for bugtask, target in query_arguments: statuses[bugtask.status][bugtask.importance].append(target) # Now that they are grouped, we will build our query. If we only have # a single bugtask target, the goal is to produce a query something # like this: # And( # Or(filter's importance = bugtask's importance, # filter's importance is null), # And( # Or(filter's status = bugtask's status, # filter's status is null), # subscription matches target)) # If there are multiple bugtask targets, but the associated bugtasks # share the same importance and status, then "subscription matches # target" will become something like this: # Or(subscription matches target 1, # subscription matches target 2, # ...) # Matching targets is done using the useful # IStructuralSubscriptionTargetHelper adapter, which has a "join" # attribute on it that tells us how to distinguish that target. outer_or_conditions = [] for status, importances in statuses.items(): inner_or_conditions = [] for importance, targets in importances.items(): target_query = Or( *[IStructuralSubscriptionTargetHelper(target).join for target in targets]) importance_query = Or( BugSubscriptionFilterImportance.importance == importance, BugSubscriptionFilterImportance.importance == None) inner_or_conditions.append(And(target_query, importance_query)) status_query = Or( BugSubscriptionFilterStatus.status == status, BugSubscriptionFilterStatus.status == None) outer_or_conditions.append( And(status_query, Or(*inner_or_conditions))) return Or(*outer_or_conditions)
def test_product(self): target = self.factory.makeProduct(owner=self.person) helper = IStructuralSubscriptionTargetHelper(target) self.assertThat(helper, Provides(IStructuralSubscriptionTargetHelper)) self.assertEqual("project", helper.target_type_display) self.assertEqual(target, helper.target) self.assertEqual(None, helper.target_parent) self.assertEqual(target, helper.pillar) self.assertEqual({"product": target}, helper.target_arguments) self.assertEqual(u"StructuralSubscription.product = ?", compile_storm(helper.join))
def test_distribution_series(self): target = self.factory.makeDistroSeries() helper = IStructuralSubscriptionTargetHelper(target) self.assertThat(helper, Provides(IStructuralSubscriptionTargetHelper)) self.assertEqual("distribution series", helper.target_type_display) self.assertEqual(target, helper.target) self.assertEqual(target.distribution, helper.target_parent) self.assertEqual({"distroseries": target}, helper.target_arguments) self.assertEqual(target.distribution, helper.pillar) self.assertEqual(u"StructuralSubscription.distroseries = ?", compile_storm(helper.join))
def get_structural_subscriptions_for_target(target, person): """Find the personal and team structural subscriptions to the target. """ # This is here because of a circular import. from lp.registry.model.person import Person return IStore(StructuralSubscription).find( StructuralSubscription, IStructuralSubscriptionTargetHelper(target).join, StructuralSubscription.subscriber == Person.id, TeamParticipation.personID == person.id, TeamParticipation.teamID == Person.id)
def __helper(self): """A `IStructuralSubscriptionTargetHelper` for this object. Eventually this helper object could become *the* way to work with structural subscriptions. For now it just provides a few bits that vary with the context. It is cached in a pseudo-private variable because this is a mixin class. """ return IStructuralSubscriptionTargetHelper(self)
def test_milestone(self): target = self.factory.makeMilestone() helper = IStructuralSubscriptionTargetHelper(target) self.assertThat(helper, Provides(IStructuralSubscriptionTargetHelper)) self.assertEqual("milestone", helper.target_type_display) self.assertEqual(target, helper.target) self.assertEqual(target.target, helper.target_parent) self.assertThat(helper.target_parent, Provides(IStructuralSubscriptionTarget)) self.assertEqual(target.target, helper.pillar) self.assertEqual({"milestone": target}, helper.target_arguments) self.assertEqual(u"StructuralSubscription.milestone = ?", compile_storm(helper.join))
def _enabled(self): """Should the link be enabled? True if the target uses Launchpad for bugs and the user can alter the bug subscriptions. """ sst = self._getSST() # ProjectGroup milestones aren't really structural subscription # targets as they're not real milestones, so you can't subscribe to # them. if IProjectGroupMilestone.providedBy(sst): return False pillar = IStructuralSubscriptionTargetHelper(sst).pillar return (pillar.bug_tracking_usage == ServiceUsage.LAUNCHPAD and sst.userCanAlterBugSubscription(self.user, self.user))
def _get_structural_subscriptions(find, targets, *conditions): """Find the structural subscriptions for the given targets. :param find: what to find (typically StructuralSubscription or StructuralSubscription.id). :param targets: an iterable of (bugtask, target) pairs, as returned by get_structural_subscription_targets. :param conditions: additional conditions to filter the results. """ targets = set(target for bugtask, target in targets) target_descriptions = [ IStructuralSubscriptionTargetHelper(bugtarget).join for bugtarget in targets] return IStore(StructuralSubscription).find( find, Or(*target_descriptions), *conditions)
def test_distribution(self): target = self.factory.makeDistribution(owner=self.person) helper = IStructuralSubscriptionTargetHelper(target) self.assertThat(helper, Provides(IStructuralSubscriptionTargetHelper)) self.assertEqual(target, helper.target) self.assertEqual("distribution", helper.target_type_display) self.assertEqual(None, helper.target_parent) self.assertEqual(target, helper.pillar) self.assertEqual({ "distribution": target, "sourcepackagename": None }, helper.target_arguments) self.assertEqual( u"StructuralSubscription.distribution = ? AND " u"StructuralSubscription.sourcepackagename IS NULL", compile_storm(helper.join))
def test_distribution_source_package(self): target = self.factory.makeDistributionSourcePackage() helper = IStructuralSubscriptionTargetHelper(target) self.assertThat(helper, Provides(IStructuralSubscriptionTargetHelper)) self.assertEqual("package", helper.target_type_display) self.assertEqual(target, helper.target) self.assertEqual(target.distribution, helper.target_parent) self.assertThat(helper.target_parent, Provides(IStructuralSubscriptionTarget)) self.assertEqual(target.distribution, helper.pillar) self.assertEqual( { "distribution": target.distribution, "sourcepackagename": target.sourcepackagename }, helper.target_arguments) self.assertEqual( u"StructuralSubscription.distribution = ? AND " u"StructuralSubscription.sourcepackagename = ?", compile_storm(helper.join))