~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-08-03 11:23:34 UTC
  • mfrom: (13457.6.16 upgrade-stderr)
  • Revision ID: launchpad@pqm.canonical.com-20110803112334-acnupsa7jmzmdeet
[r=stevenk][bug=819751] Fix the implementation of several methods in
 LoggingUIFactory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
# pylint: disable-msg=E0211,E0213,E0602
17
17
    'BugTaskStatus',
18
18
    'BugTaskStatusSearch',
19
19
    'BugTaskStatusSearchDisplay',
20
 
    'CannotDeleteBugtask',
21
 
    'DB_INCOMPLETE_BUGTASK_STATUSES',
22
 
    'DB_UNRESOLVED_BUGTASK_STATUSES',
23
20
    'DEFAULT_SEARCH_BUGTASK_STATUSES_FOR_DISPLAY',
24
 
    'get_bugtask_status',
25
21
    'IAddBugTaskForm',
26
22
    'IAddBugTaskWithProductCreationForm',
27
23
    'IBugTask',
28
 
    'IBugTaskDelete',
29
24
    'IBugTaskDelta',
30
25
    'IBugTaskSearch',
31
26
    'IBugTaskSet',
32
27
    'ICreateQuestionFromBugTaskForm',
33
28
    'IFrontPageBugTaskSearch',
34
 
    'IllegalRelatedBugTasksParams',
35
 
    'IllegalTarget',
36
29
    'INominationsReviewTableBatchNavigator',
37
30
    'IPersonBugTaskSearch',
38
31
    'IRemoveQuestionFromBugTaskForm',
39
32
    'IUpstreamProductBugTaskSearch',
40
 
    'normalize_bugtask_status',
 
33
    'IllegalRelatedBugTasksParams',
 
34
    'IllegalTarget',
41
35
    'RESOLVED_BUGTASK_STATUSES',
42
36
    'UNRESOLVED_BUGTASK_STATUSES',
43
37
    'UserCannotEditBugTaskAssignee',
60
54
    call_with,
61
55
    error_status,
62
56
    export_as_webservice_entry,
63
 
    export_destructor_operation,
64
57
    export_read_operation,
65
58
    export_write_operation,
66
59
    exported,
99
92
from zope.security.interfaces import Unauthorized
100
93
from zope.security.proxy import isinstance as zope_isinstance
101
94
 
102
 
from lp import _
103
 
from lp.app.interfaces.launchpad import IHasDateCreated
 
95
from canonical.launchpad import _
 
96
from canonical.launchpad.interfaces.launchpad import (
 
97
    IHasBug,
 
98
    IHasDateCreated,
 
99
    )
 
100
from canonical.launchpad.searchbuilder import (
 
101
    all,
 
102
    any,
 
103
    NULL,
 
104
    )
 
105
from canonical.launchpad.webapp.interfaces import ITableBatchNavigator
104
106
from lp.app.validators import LaunchpadValidationError
105
107
from lp.app.validators.name import name_validator
106
108
from lp.bugs.interfaces.bugwatch import (
109
111
    NoBugTrackerFound,
110
112
    UnrecognizedBugTrackerURL,
111
113
    )
112
 
from lp.bugs.interfaces.hasbug import IHasBug
113
114
from lp.services.fields import (
114
115
    BugField,
115
116
    PersonChoice,
118
119
    StrippedTextLine,
119
120
    Summary,
120
121
    )
121
 
from lp.services.searchbuilder import (
122
 
    all,
123
 
    any,
124
 
    NULL,
125
 
    )
126
 
from lp.services.webapp.interfaces import ITableBatchNavigator
127
122
from lp.soyuz.interfaces.component import IComponent
128
123
 
129
124
 
201
196
        this product or source package.
202
197
        """)
203
198
 
204
 
    # INCOMPLETE is never actually stored now: INCOMPLETE_WITH_RESPONSE and
205
 
    # INCOMPLETE_WITHOUT_RESPONSE are mapped to INCOMPLETE on read, and on
206
 
    # write INCOMPLETE is mapped to INCOMPLETE_WITHOUT_RESPONSE. This permits
207
 
    # An index on the INCOMPLETE_WITH*_RESPONSE queries that the webapp
208
 
    # generates.
209
199
    INCOMPLETE = DBItem(15, """
210
200
        Incomplete
211
201
 
279
269
        affected software.
280
270
        """)
281
271
 
 
272
    # DBItem values 35 and 40 are used by
 
273
    # BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE and
 
274
    # BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE
 
275
 
282
276
    UNKNOWN = DBItem(999, """
283
277
        Unknown
284
278
 
293
287
    """
294
288
    use_template(BugTaskStatus, exclude=('UNKNOWN'))
295
289
 
296
 
    INCOMPLETE_WITH_RESPONSE = DBItem(13, """
 
290
    sort_order = (
 
291
        'NEW', 'INCOMPLETE_WITH_RESPONSE', 'INCOMPLETE_WITHOUT_RESPONSE',
 
292
        'INCOMPLETE', 'OPINION', 'INVALID', 'WONTFIX', 'EXPIRED',
 
293
        'CONFIRMED', 'TRIAGED', 'INPROGRESS', 'FIXCOMMITTED', 'FIXRELEASED')
 
294
 
 
295
    INCOMPLETE_WITH_RESPONSE = DBItem(35, """
297
296
        Incomplete (with response)
298
297
 
299
298
        This bug has new information since it was last marked
300
299
        as requiring a response.
301
300
        """)
302
301
 
303
 
    INCOMPLETE_WITHOUT_RESPONSE = DBItem(14, """
 
302
    INCOMPLETE_WITHOUT_RESPONSE = DBItem(40, """
304
303
        Incomplete (without response)
305
304
 
306
305
        This bug requires more information, but no additional
308
307
        """)
309
308
 
310
309
 
311
 
def get_bugtask_status(status_id):
312
 
    """Get a member of `BugTaskStatus` or `BugTaskStatusSearch` by value.
313
 
 
314
 
    `BugTaskStatus` and `BugTaskStatusSearch` intersect, but neither is a
315
 
    subset of the other, so this searches first in `BugTaskStatus` then in
316
 
    `BugTaskStatusSearch` for a member with the given ID.
317
 
    """
318
 
    try:
319
 
        return BugTaskStatus.items[status_id]
320
 
    except KeyError:
321
 
        return BugTaskStatusSearch.items[status_id]
322
 
 
323
 
 
324
 
def normalize_bugtask_status(status):
325
 
    """Normalize `status`.
326
 
 
327
 
    It might be a member of any of three related enums: `BugTaskStatus`,
328
 
    `BugTaskStatusSearch`, or `BugTaskStatusSearchDisplay`. This tries to
329
 
    normalize by value back to the first of those three enums in which the
330
 
    status appears.
331
 
    """
332
 
    try:
333
 
        return BugTaskStatus.items[status.value]
334
 
    except KeyError:
335
 
        return BugTaskStatusSearch.items[status.value]
336
 
 
337
 
 
338
310
class BugTagsSearchCombinator(EnumeratedType):
339
311
    """Bug Tags Search Combinator
340
312
 
399
371
    BugTaskStatus.INPROGRESS,
400
372
    BugTaskStatus.FIXCOMMITTED)
401
373
 
402
 
# Actual values stored in the DB:
403
 
DB_INCOMPLETE_BUGTASK_STATUSES = (
404
 
    BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE,
405
 
    BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE,
406
 
    )
407
 
 
408
 
DB_UNRESOLVED_BUGTASK_STATUSES = (
409
 
    UNRESOLVED_BUGTASK_STATUSES +
410
 
    DB_INCOMPLETE_BUGTASK_STATUSES
411
 
    )
412
 
 
413
374
RESOLVED_BUGTASK_STATUSES = (
414
375
    BugTaskStatus.FIXRELEASED,
415
376
    BugTaskStatus.OPINION,
436
397
    for item in DEFAULT_SEARCH_BUGTASK_STATUSES]
437
398
 
438
399
 
439
 
@error_status(httplib.BAD_REQUEST)
440
 
class CannotDeleteBugtask(Exception):
441
 
    """The bugtask cannot be deleted.
442
 
 
443
 
    Raised when a user tries to delete a bugtask but the deletion cannot
444
 
    proceed because of a model constraint or other business rule violation.
445
 
    """
446
 
 
447
 
 
448
400
@error_status(httplib.UNAUTHORIZED)
449
401
class UserCannotEditBugTaskStatus(Unauthorized):
450
402
    """User not permitted to change status.
492
444
    in a search for related bug tasks"""
493
445
 
494
446
 
495
 
class IBugTaskDelete(Interface):
496
 
    """An interface for operations allowed with the Delete permission."""
497
 
    @export_destructor_operation()
498
 
    @call_with(who=REQUEST_USER)
499
 
    @operation_for_version('devel')
500
 
    def delete(who):
501
 
        """Delete this bugtask.
502
 
 
503
 
        :param who: the user who is removing the bugtask.
504
 
        :raises: CannotDeleteBugtask if the bugtask cannot be deleted due to a
505
 
            business rule or other model constraint.
506
 
        :raises: Unauthorized if the user does not have permission
507
 
            to delete the bugtask.
508
 
        """
509
 
 
510
 
 
511
 
class IBugTask(IHasDateCreated, IHasBug, IBugTaskDelete):
 
447
class IBugTask(IHasDateCreated, IHasBug):
512
448
    """A bug needing fixing in a particular product or package."""
513
449
    export_as_webservice_entry()
514
450
 
545
481
    # bugwatch; this would be better described in a separate interface,
546
482
    # but adding a marker interface during initialization is expensive,
547
483
    # and adding it post-initialization is not trivial.
548
 
    # Note that status is a property because the model only exposes INCOMPLETE
549
 
    # but the DB stores INCOMPLETE_WITH_RESPONSE and
550
 
    # INCOMPLETE_WITHOUT_RESPONSE for query efficiency.
551
484
    status = exported(
552
485
        Choice(title=_('Status'), vocabulary=BugTaskStatus,
553
486
               default=BugTaskStatus.NEW, readonly=True))
554
 
    _status = Attribute('The actual status DB column used in queries.')
555
487
    importance = exported(
556
488
        Choice(title=_('Importance'), vocabulary=BugTaskImportance,
557
489
               default=BugTaskImportance.UNDECIDED, readonly=True))
 
490
    statusexplanation = Text(
 
491
        title=_("Status notes (optional)"), required=False)
558
492
    assignee = exported(
559
493
        PersonChoice(
560
494
            title=_('Assigned to'), required=False,
911
845
        not a package task, returns None.
912
846
        """
913
847
 
914
 
    def userHasDriverPrivileges(user):
915
 
        """Does the user have driver privledges on the current bugtask?
916
 
 
917
 
        :return: A boolean.
918
 
        """
919
 
 
920
 
    def userHasBugSupervisorPrivileges(user):
921
 
        """Is the user a privledged one, allowed to changed details on a
922
 
        bug?
923
 
 
924
 
        :return: A boolean.
925
 
        """
 
848
    def userCanEditMilestone(user):
 
849
        """Can the user edit the Milestone field?"""
 
850
 
 
851
    def userCanEditImportance(user):
 
852
        """Can the user edit the Importance field?"""
926
853
 
927
854
 
928
855
# Set schemas that were impossible to specify during the definition of
993
920
    omit_targeted = Bool(
994
921
        title=_('Omit bugs targeted to a series'), required=False,
995
922
        default=True)
 
923
    statusexplanation = TextLine(
 
924
        title=_("Status notes"), required=False)
996
925
    has_patch = Bool(
997
926
        title=_('Show only bugs with patches available.'), required=False,
998
927
        default=False)
1021
950
        required=False)
1022
951
    has_cve = Bool(
1023
952
        title=_('Show only bugs associated with a CVE'), required=False)
1024
 
    structural_subscriber = Choice(
1025
 
        title=_('Structural Subscriber'), vocabulary='ValidPersonOrTeam',
1026
 
        description=_(
1027
 
            'Show only bugs in projects, series, distributions, and packages '
1028
 
            'that this person or team is subscribed to.'),
 
953
    bug_supervisor = Choice(
 
954
        title=_('Bug supervisor'), vocabulary='ValidPersonOrTeam',
 
955
        description=_('Show only bugs in packages this person or team '
 
956
                      'is subscribed to.'),
1029
957
        required=False)
1030
958
    bug_commenter = Choice(
1031
959
        title=_('Bug commenter'), vocabulary='ValidPersonOrTeam',
1102
1030
        required=False)
1103
1031
 
1104
1032
 
1105
 
class IFrontPageBugTaskSearch(IBugTaskSearch):
 
1033
class IFrontPageBugTaskSearch(IBugTaskSearchBase):
1106
1034
    """Additional search options for the front page of bugs."""
1107
1035
    scope = Choice(
1108
1036
        title=u"Search Scope", required=False,
1143
1071
        The value is a dict like {'old' : IPerson, 'new' : IPerson}, or None,
1144
1072
        if no change was made to the assignee.
1145
1073
        """)
 
1074
    statusexplanation = Attribute("The new value of the status notes.")
1146
1075
    bugwatch = Attribute("The bugwatch which governs this task.")
1147
1076
    milestone = Attribute("The milestone for which this task is scheduled.")
1148
1077
 
1481
1410
class IBugTaskSet(Interface):
1482
1411
    """A utility to retrieving BugTasks."""
1483
1412
    title = Attribute('Title')
1484
 
    orderby_expression = Attribute(
1485
 
        "The SQL expression for a sort key")
1486
1413
 
1487
1414
    def get(task_id):
1488
1415
        """Retrieve a BugTask with the given id.
1639
1566
        <user> is None, no private bugtasks will be returned.
1640
1567
        """
1641
1568
 
 
1569
    def getOrderByColumnDBName(col_name):
 
1570
        """Get the database name for col_name.
 
1571
 
 
1572
        If the col_name is unrecognized, a KeyError is raised.
 
1573
        """
 
1574
 
1642
1575
    def getBugCountsForPackages(user, packages):
1643
1576
        """Return open bug counts for the list of packages.
1644
1577
 
1664
1597
        The assignee and the assignee's validity are precached.
1665
1598
        """
1666
1599
 
1667
 
    def getBugTaskTargetMilestones(bugtasks):
 
1600
    def getBugTaskTargetMilestones(self, bugtasks, eager=False):
1668
1601
        """Get all the milestones for the selected bugtasks' targets."""
1669
1602
 
1670
1603
    open_bugtask_search = Attribute("A search returning open bugTasks.")