~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

Merged bug-824435-failure-reporting-2 into bug-824435-failure-reporting-3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
135
135
from lp.registry.interfaces.distributionsourcepackage import (
136
136
    IDistributionSourcePackage,
137
137
    )
138
 
from lp.registry.interfaces.distroseries import (
139
 
    IDistroSeries,
140
 
    IDistroSeriesSet,
141
 
    )
 
138
from lp.registry.interfaces.distroseries import IDistroSeries
142
139
from lp.registry.interfaces.milestone import (
143
140
    IMilestoneSet,
144
141
    IProjectGroupMilestone,
148
145
    validate_person,
149
146
    validate_public_person,
150
147
    )
151
 
from lp.registry.interfaces.product import (
152
 
    IProduct,
153
 
    IProductSet,
154
 
    )
155
 
from lp.registry.interfaces.productseries import (
156
 
    IProductSeries,
157
 
    IProductSeriesSet,
158
 
    )
 
148
from lp.registry.interfaces.product import IProduct
 
149
from lp.registry.interfaces.productseries import IProductSeries
159
150
from lp.registry.interfaces.projectgroup import IProjectGroup
160
151
from lp.registry.interfaces.sourcepackage import ISourcePackage
161
152
from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
339
330
    return bugtask.bug
340
331
 
341
332
 
342
 
@block_implicit_flushes
343
 
def validate_target_attribute(self, attr, value):
344
 
    """Update the targetnamecache."""
345
 
    # Don't update targetnamecache during _init().
346
 
    if self._SO_creating or self._inhibit_target_check:
347
 
        return value
348
 
    # Determine the new target attributes.
349
 
    target_params = dict(
350
 
        product=self.product,
351
 
        productseries=self.productseries,
352
 
        sourcepackagename=self.sourcepackagename,
353
 
        distribution=self.distribution,
354
 
        distroseries=self.distroseries)
355
 
    utility_iface_dict = {
356
 
        'productID': IProductSet,
357
 
        'productseriesID': IProductSeriesSet,
358
 
        'sourcepackagenameID': ISourcePackageNameSet,
359
 
        'distributionID': IDistributionSet,
360
 
        'distroseriesID': IDistroSeriesSet,
361
 
        }
362
 
    utility_iface = utility_iface_dict[attr]
363
 
    if value is None:
364
 
        target_params[attr[:-2]] = None
365
 
    else:
366
 
        target_params[attr[:-2]] = getUtility(utility_iface).get(value)
367
 
 
368
 
    # Update the target name cache with the potential new target. The
369
 
    # attribute changes haven't been made yet, so we need to calculate the
370
 
    # target manually.
371
 
    self.updateTargetNameCache(bug_target_from_key(**target_params))
372
 
 
373
 
    return value
374
 
 
375
 
 
376
333
class PassthroughValue:
377
334
    """A wrapper to allow setting values on conjoined bug tasks."""
378
335
 
400
357
    # people try to update the conjoined slave via the API.
401
358
    conjoined_master = self.conjoined_master
402
359
    if conjoined_master is not None:
403
 
        setattr(self.conjoined_master, attr, value)
 
360
        setattr(conjoined_master, attr, value)
404
361
        return value
405
362
 
406
 
    # The conjoined slave is updated before the master one because,
407
 
    # for distro tasks, conjoined_slave does a comparison on
408
 
    # sourcepackagename, and the sourcepackagenames will not match
409
 
    # if the conjoined master is altered before the conjoined slave!
 
363
    # If there is a conjoined slave, update that.
410
364
    conjoined_bugtask = self.conjoined_slave
411
365
    if conjoined_bugtask:
412
366
        setattr(conjoined_bugtask, attr, PassthroughValue(value))
427
381
    return validate_person(self, attr, value)
428
382
 
429
383
 
430
 
@block_implicit_flushes
431
 
def validate_sourcepackagename(self, attr, value):
432
 
    is_passthrough = isinstance(value, PassthroughValue)
433
 
    value = validate_conjoined_attribute(self, attr, value)
434
 
    if not is_passthrough:
435
 
        self._syncSourcePackages(value)
436
 
    return validate_target_attribute(self, attr, value)
437
 
 
438
 
 
439
384
def validate_target(bug, target):
440
385
    """Validate a bugtask target against a bug's existing tasks.
441
386
 
446
391
            "A fix for this bug has already been requested for %s"
447
392
            % target.displayname)
448
393
 
449
 
    if IDistributionSourcePackage.providedBy(target):
 
394
    if (IDistributionSourcePackage.providedBy(target) or
 
395
        ISourcePackage.providedBy(target)):
450
396
        # If the distribution has at least one series, check that the
451
397
        # source package has been published in the distribution.
452
398
        if (target.sourcepackagename is not None and
514
460
    bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True)
515
461
    product = ForeignKey(
516
462
        dbName='product', foreignKey='Product',
517
 
        notNull=False, default=None,
518
 
        storm_validator=validate_target_attribute)
 
463
        notNull=False, default=None)
519
464
    productseries = ForeignKey(
520
465
        dbName='productseries', foreignKey='ProductSeries',
521
 
        notNull=False, default=None,
522
 
        storm_validator=validate_target_attribute)
 
466
        notNull=False, default=None)
523
467
    sourcepackagename = ForeignKey(
524
468
        dbName='sourcepackagename', foreignKey='SourcePackageName',
525
 
        notNull=False, default=None,
526
 
        storm_validator=validate_sourcepackagename)
 
469
        notNull=False, default=None)
527
470
    distribution = ForeignKey(
528
471
        dbName='distribution', foreignKey='Distribution',
529
 
        notNull=False, default=None,
530
 
        storm_validator=validate_target_attribute)
 
472
        notNull=False, default=None)
531
473
    distroseries = ForeignKey(
532
474
        dbName='distroseries', foreignKey='DistroSeries',
533
 
        notNull=False, default=None,
534
 
        storm_validator=validate_target_attribute)
 
475
        notNull=False, default=None)
535
476
    milestone = ForeignKey(
536
477
        dbName='milestone', foreignKey='Milestone',
537
478
        notNull=False, default=None,
712
653
        """See `IBugTask`."""
713
654
        return self.bug.isSubscribed(person)
714
655
 
715
 
    def _syncSourcePackages(self, new_spnid):
 
656
    def _syncSourcePackages(self, new_spn):
716
657
        """Synchronize changes to source packages with other distrotasks.
717
658
 
718
659
        If one distroseriestask's source package is changed, all the
720
661
        package has to be changed, as well as the corresponding
721
662
        distrotask.
722
663
        """
723
 
        if self.bug is None:
724
 
            # The validator is being called on an incomplete bug task.
 
664
        if self.bug is None or not (self.distribution or self.distroseries):
 
665
            # The validator is being called on a new or non-distro task.
725
666
            return
726
 
        if self.distroseries is not None:
727
 
            distribution = self.distroseries.distribution
728
 
        else:
729
 
            distribution = self.distribution
730
 
        if distribution is not None:
731
 
            for bugtask in self.related_tasks:
732
 
                if bugtask.distroseries:
733
 
                    related_distribution = bugtask.distroseries.distribution
734
 
                else:
735
 
                    related_distribution = bugtask.distribution
736
 
                if (related_distribution == distribution and
737
 
                    bugtask.sourcepackagenameID == self.sourcepackagenameID):
738
 
                    bugtask.sourcepackagenameID = PassthroughValue(new_spnid)
 
667
        distribution = self.distribution or self.distroseries.distribution
 
668
        for bugtask in self.related_tasks:
 
669
            relevant = (
 
670
                bugtask.sourcepackagename == self.sourcepackagename and
 
671
                distribution in (
 
672
                    bugtask.distribution,
 
673
                    getattr(bugtask.distroseries, 'distribution', None)))
 
674
            if relevant:
 
675
                key = bug_target_to_key(bugtask.target)
 
676
                key['sourcepackagename'] = new_spn
 
677
                bugtask.transitionToTarget(
 
678
                    bug_target_from_key(**key),
 
679
                    _sync_sourcepackages=False)
739
680
 
740
681
    def getContributorInfo(self, user, person):
741
682
        """See `IBugTask`."""
1200
1141
 
1201
1142
        validate_target(self.bug, target)
1202
1143
 
1203
 
    def transitionToTarget(self, target):
 
1144
    def transitionToTarget(self, target, _sync_sourcepackages=True):
1204
1145
        """See `IBugTask`.
1205
1146
 
1206
 
        This method allows changing the target of some bug
1207
 
        tasks. The rules it follows are similar to the ones
1208
 
        enforced implicitly by the code in
1209
 
        lib/canonical/launchpad/browser/bugtask.py#BugTaskEditView.
 
1147
        If _sync_sourcepackages is True (the default) and the
 
1148
        sourcepackagename is being changed, any other tasks for the same
 
1149
        name in this distribution will have their names updated to
 
1150
        match. This should only be used by _syncSourcePackages.
1210
1151
        """
1211
1152
 
1212
1153
        if self.target == target:
1223
1164
            # current target, or reset it to None
1224
1165
            self.milestone = None
1225
1166
 
1226
 
        # Inhibit validate_target_attribute, as we can't set them all
1227
 
        # atomically, but we know the final result is correct.
1228
 
        self._inhibit_target_check = True
1229
 
        for name, value in bug_target_to_key(target).iteritems():
 
1167
        new_key = bug_target_to_key(target)
 
1168
 
 
1169
        # As a special case, if the sourcepackagename has changed then
 
1170
        # we update any other tasks for the same distribution and
 
1171
        # sourcepackagename. This keeps series tasks consistent.
 
1172
        if (_sync_sourcepackages and
 
1173
            new_key['sourcepackagename'] != self.sourcepackagename):
 
1174
            self._syncSourcePackages(new_key['sourcepackagename'])
 
1175
 
 
1176
        for name, value in new_key.iteritems():
1230
1177
            setattr(self, name, value)
1231
 
        self._inhibit_target_check = False
1232
1178
        self.updateTargetNameCache()
1233
1179
 
1234
1180
        # After the target has changed, we need to recalculate the maximum bug