~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/bugs/model/bugtask.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-07-19 20:31:43 UTC
  • mfrom: (13445.1.10 bug777874)
  • Revision ID: launchpad@pqm.canonical.com-20110719203143-uwkz5us2ewbgx50v
[r=gmb][bug=777874] enable autocompletion of "New" bugtasks that
        affect more than one person. Feature-flagged by project/distro.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import datetime
25
25
from itertools import chain
26
26
from operator import attrgetter
 
27
import re
27
28
 
28
29
from lazr.enum import BaseItem
 
30
from lazr.lifecycle.event import ObjectModifiedEvent
 
31
from lazr.lifecycle.snapshot import Snapshot
29
32
import pytz
30
33
from sqlobject import (
31
34
    ForeignKey,
53
56
    Store,
54
57
    )
55
58
from zope.component import getUtility
 
59
from zope.event import notify
56
60
from zope.interface import (
57
61
    alsoProvides,
58
62
    implements,
 
63
    providedBy,
59
64
    )
60
65
from zope.security.proxy import (
61
66
    isinstance as zope_isinstance,
263
268
        raise IllegalRelatedBugTasksParams(
264
269
            ('Cannot search for related tasks to \'%s\', at least one '
265
270
             'of these parameter has to be empty: %s'
266
 
                %(context.name, ", ".join(relevant_fields))))
 
271
                % (context.name, ", ".join(relevant_fields))))
267
272
    return search_params
268
273
 
269
274
 
814
819
            raise ValueError('Unknown debbugs severity "%s".' % severity)
815
820
        return self.importance
816
821
 
 
822
    # START TEMPORARY BIT FOR BUGTASK AUTOCONFIRM FEATURE FLAG.
 
823
    _parse_launchpad_names = re.compile(r"[a-z0-9][a-z0-9\+\.\-]+").findall
 
824
 
 
825
    def _checkAutoconfirmFeatureFlag(self):
 
826
        """Does a feature flag enable automatic switching of our bugtasks?"""
 
827
        # This method should be ripped out if we determine that we like
 
828
        # this behavior for all projects.
 
829
        # This is a bit of a feature flag hack, but has been discussed as
 
830
        # a reasonable way to deploy this quickly.
 
831
        pillar = self.pillar
 
832
        if IDistribution.providedBy(pillar):
 
833
            flag_name = 'bugs.autoconfirm.enabled_distribution_names'
 
834
        else:
 
835
            assert IProduct.providedBy(pillar), 'unexpected pillar'
 
836
            flag_name = 'bugs.autoconfirm.enabled_product_names'
 
837
        enabled = features.getFeatureFlag(flag_name)
 
838
        if enabled is None:
 
839
            return False
 
840
        if (enabled.strip() != '*' and
 
841
            pillar.name not in self._parse_launchpad_names(enabled)):
 
842
            # We are not generically enabled ('*') and our pillar's name
 
843
            # is not explicitly enabled.
 
844
            return False
 
845
        return True
 
846
    # END TEMPORARY BIT FOR BUGTASK AUTOCONFIRM FEATURE FLAG.
 
847
 
 
848
    def maybeConfirm(self):
 
849
        """Maybe confirm this bugtask.
 
850
        Only call this if the bug._shouldConfirmBugtasks().
 
851
        This adds the further constraint that the bugtask needs to be NEW,
 
852
        and not imported from an external bug tracker.
 
853
        """
 
854
        if (self.status == BugTaskStatus.NEW
 
855
            and self.bugwatch is None
 
856
            # START TEMPORARY BIT FOR BUGTASK AUTOCONFIRM FEATURE FLAG.
 
857
            and self._checkAutoconfirmFeatureFlag()
 
858
            # END TEMPORARY BIT FOR BUGTASK AUTOCONFIRM FEATURE FLAG.
 
859
            ):
 
860
            user = getUtility(ILaunchpadCelebrities).janitor
 
861
            bugtask_before_modification = Snapshot(
 
862
                self, providing=providedBy(self))
 
863
            self.transitionToStatus(BugTaskStatus.CONFIRMED, user)
 
864
            notify(ObjectModifiedEvent(
 
865
                self, bugtask_before_modification, ['status'], user=user))
 
866
 
817
867
    def canTransitionToStatus(self, new_status, user):
818
868
        """See `IBugTask`."""
819
869
        celebrities = getUtility(ILaunchpadCelebrities)
1079
1129
        if self.target != target_before_change:
1080
1130
            target_before_change.recalculateBugHeatCache()
1081
1131
            self.target.recalculateBugHeatCache()
 
1132
            # START TEMPORARY BIT FOR BUGTASK AUTOCONFIRM FEATURE FLAG.
 
1133
            # We also should see if we ought to auto-transition to the
 
1134
            # CONFIRMED status.
 
1135
            if self.bug.shouldConfirmBugtasks():
 
1136
                self.maybeConfirm()
 
1137
            # END TEMPORARY BIT FOR BUGTASK AUTOCONFIRM FEATURE FLAG.
1082
1138
 
1083
1139
    def updateTargetNameCache(self, newtarget=None):
1084
1140
        """See `IBugTask`."""
1791
1847
                "BugTaskSearchParam.exclude_conjoined cannot be True if "
1792
1848
                "BugTaskSearchParam.milestone is not set")
1793
1849
 
1794
 
 
1795
1850
        if params.project:
1796
1851
            # Prevent circular import problems.
1797
1852
            from lp.registry.model.product import Product
1895
1950
                % sqlvalues(params.structural_subscriber))
1896
1951
            has_duplicate_results = True
1897
1952
 
1898
 
 
1899
1953
        # Remove bugtasks from deactivated products, if necessary.
1900
1954
        # We don't have to do this if
1901
1955
        # 1) We're searching on bugtasks for a specific product
2531
2585
        from lp.bugs.model.bugsummary import BugSummary
2532
2586
        conditions = []
2533
2587
        # Open bug statuses
2534
 
        conditions.append(BugSummary.status.is_in(UNRESOLVED_BUGTASK_STATUSES))
 
2588
        conditions.append(
 
2589
            BugSummary.status.is_in(UNRESOLVED_BUGTASK_STATUSES))
2535
2590
        # BugSummary does not include duplicates so no need to exclude.
2536
2591
        context_conditions = []
2537
2592
        for context in contexts:
2564
2619
                "teams AS ("
2565
2620
                "SELECT team from TeamParticipation WHERE person=?)",
2566
2621
                (user.id,)))
2567
 
        # Note that because admins can see every bug regardless of subscription
2568
 
        # they will see rather inflated counts. Admins get to deal.
 
2622
        # Note that because admins can see every bug regardless of
 
2623
        # subscription they will see rather inflated counts. Admins get to
 
2624
        # deal.
2569
2625
        if user is None:
2570
2626
            conditions.append(BugSummary.viewed_by_id == None)
2571
2627
        elif not user.inTeam(admin_team):
2572
2628
            conditions.append(
2573
2629
                Or(
2574
2630
                    BugSummary.viewed_by_id == None,
2575
 
                    BugSummary.viewed_by_id.is_in(SQL("SELECT team FROM teams"))
 
2631
                    BugSummary.viewed_by_id.is_in(
 
2632
                        SQL("SELECT team FROM teams"))
2576
2633
                    ))
2577
2634
        sum_count = Sum(BugSummary.count)
2578
2635
        resultset = store.find(group_on + (sum_count,), *conditions)
3189
3246
        distro_series_ids = set()
3190
3247
        product_ids = set()
3191
3248
        product_series_ids = set()
3192
 
        
 
3249
 
3193
3250
        # Gather all the ids that might have milestones to preload for the
3194
3251
        # for the milestone vocabulary
3195
3252
        for task in bugtasks:
3199
3256
            product_ids.add(task.productID)
3200
3257
            product_series_ids.add(task.productseriesID)
3201
3258
 
3202
 
        distro_ids.discard(None) 
3203
 
        distro_series_ids.discard(None) 
3204
 
        product_ids.discard(None) 
3205
 
        product_series_ids.discard(None) 
3206
 
        
 
3259
        distro_ids.discard(None)
 
3260
        distro_series_ids.discard(None)
 
3261
        product_ids.discard(None)
 
3262
        product_series_ids.discard(None)
 
3263
 
3207
3264
        milestones = store.find(
3208
3265
            Milestone,
3209
3266
            Or(
3221
3278
            Product, Product.id.is_in(product_ids)))
3222
3279
        list(store.find(
3223
3280
            ProductSeries, ProductSeries.id.is_in(product_series_ids)))
3224
 
            
 
3281
 
3225
3282
        return milestones
3226
 
 
3227