~launchpad-pqm/launchpad/devel

8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4983.1.2 by Curtis Hovey
Added pylint exceptions to database classes.
4
# pylint: disable-msg=E0611,W0212
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
5
6
"""Hardware database related table classes."""
7
6130.3.3 by Abel Deuring
implemented reviewer's comments
8
__all__ = [
6130.10.3 by Abel Deuring
implemented reviewr's comments
9
    'HWDevice',
6740.4.1 by Abel Deuring
core implementation of class HWDeviceClass
10
    'HWDeviceClass',
6130.10.3 by Abel Deuring
implemented reviewr's comments
11
    'HWDeviceSet',
12
    'HWDeviceDriverLink',
13
    'HWDeviceDriverLinkSet',
14
    'HWDeviceNameVariant',
15
    'HWDeviceNameVariantSet',
16
    'HWDriver',
8538.3.1 by Abel Deuring
Python classes for SQL views hwdrivername and hwdriverpackagename added
17
    'HWDriverName',
18
    'HWDriverPackageName',
6130.10.3 by Abel Deuring
implemented reviewr's comments
19
    'HWDriverSet',
6130.3.3 by Abel Deuring
implemented reviewer's comments
20
    'HWSubmission',
6796.1.1 by Abel Deuring
Definition of HWSubmissionBug, linking HWDB submissions with bugs
21
    'HWSubmissionBug',
22
    'HWSubmissionBugSet',
6130.3.3 by Abel Deuring
implemented reviewer's comments
23
    'HWSubmissionSet',
6130.10.3 by Abel Deuring
implemented reviewr's comments
24
    'HWSubmissionDevice',
25
    'HWSubmissionDeviceSet',
6130.3.3 by Abel Deuring
implemented reviewer's comments
26
    'HWSystemFingerprint',
27
    'HWSystemFingerprintSet',
28
    'HWVendorID',
29
    'HWVendorIDSet',
30
    'HWVendorName',
31
    'HWVendorNameSet',
8697.20.3 by Abel Deuring
moved the code to generate hardware related search clauses from l.c.l.hwdb to lp.bugs.model.bugtask
32
    'make_submission_device_statistics_clause',
33
    '_userCanAccessSubmissionStormClause',
6130.3.3 by Abel Deuring
implemented reviewer's comments
34
    ]
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
35
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
36
import re
37
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
38
from sqlobject import (
39
    BoolCol,
40
    ForeignKey,
41
    IntCol,
42
    StringCol,
43
    )
44
from storm.expr import (
45
    Alias,
46
    And,
47
    Count,
48
    Not,
49
    Or,
50
    Select,
51
    )
52
from storm.store import Store
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
53
from zope.component import getUtility
54
from zope.interface import implements
55
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
56
from canonical.database.constants import (
57
    DEFAULT,
58
    UTC_NOW,
59
    )
5852.2.2 by James Henstridge
fix some columns that claimed to be notNull=True but are in fact not not null
60
from canonical.database.datetimecol import UtcDateTimeCol
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
61
from canonical.database.enumcol import EnumCol
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
62
from canonical.database.sqlbase import (
63
    SQLBase,
64
    sqlvalues,
65
    )
66
from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
67
from canonical.launchpad.webapp.interfaces import (
68
    DEFAULT_FLAVOR,
69
    IStoreSelector,
70
    MAIN_STORE,
71
    )
13130.1.12 by Curtis Hovey
Sorted imports.
72
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
12442.2.9 by j.c.sackett
Ran import reformatter per review.
73
from lp.app.validators.name import valid_name
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
74
from lp.bugs.model.bug import (
75
    Bug,
76
    BugAffectsPerson,
77
    BugTag,
78
    )
8523.3.1 by Gavin Panella
Bugs tree reorg after automated migration.
79
from lp.bugs.model.bugsubscription import BugSubscription
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
80
from lp.hardwaredb.interfaces.hwdb import (
81
    HWBus,
82
    HWSubmissionFormat,
83
    HWSubmissionKeyNotUnique,
84
    HWSubmissionProcessingStatus,
85
    IHWDevice,
86
    IHWDeviceClass,
87
    IHWDeviceClassSet,
88
    IHWDeviceDriverLink,
89
    IHWDeviceDriverLinkSet,
90
    IHWDeviceNameVariant,
91
    IHWDeviceNameVariantSet,
92
    IHWDeviceSet,
93
    IHWDriver,
94
    IHWDriverName,
95
    IHWDriverPackageName,
96
    IHWDriverSet,
97
    IHWSubmission,
98
    IHWSubmissionBug,
99
    IHWSubmissionBugSet,
100
    IHWSubmissionDevice,
101
    IHWSubmissionDeviceSet,
102
    IHWSubmissionSet,
103
    IHWSystemFingerprint,
104
    IHWSystemFingerprintSet,
105
    IHWVendorID,
106
    IHWVendorIDSet,
107
    IHWVendorName,
108
    IHWVendorNameSet,
109
    IllegalQuery,
110
    ParameterError,
111
    )
112
from lp.registry.interfaces.distribution import IDistribution
113
from lp.registry.interfaces.distroseries import IDistroSeries
114
from lp.registry.interfaces.person import (
115
    IPersonSet,
116
    validate_public_person,
117
    )
118
from lp.registry.interfaces.product import License
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
119
from lp.registry.model.distribution import Distribution
120
from lp.registry.model.distroseries import DistroSeries
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
121
from lp.registry.model.person import Person
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
122
from lp.registry.model.teammembership import TeamParticipation
8303.14.7 by Julian Edwards
Fix imports broken in the trunk merge.
123
from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
124
from lp.soyuz.model.distroarchseries import DistroArchSeries
4974.3.4 by Abel Deuring
implemented reviewer's comments
125
6130.3.3 by Abel Deuring
implemented reviewer's comments
126
# The vendor name assigned to new, unknown vendor IDs. See
127
# HWDeviceSet.create().
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
128
UNKNOWN = 'Unknown'
129
130
4675.1.3 by Abel Deuring
schema modifications by stub
131
class HWSubmission(SQLBase):
4675.1.4 by Abel Deuring
implemented reviewer's comments
132
    """See `IHWSubmission`."""
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
133
4675.1.3 by Abel Deuring
schema modifications by stub
134
    implements(IHWSubmission)
6130.3.5 by Abel Deuring
implemented reviewer's comments
135
4675.1.3 by Abel Deuring
schema modifications by stub
136
    _table = 'HWSubmission'
5852.2.2 by James Henstridge
fix some columns that claimed to be notNull=True but are in fact not not null
137
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
138
    date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW)
139
    date_submitted = UtcDateTimeCol(notNull=True, default=UTC_NOW)
4675.1.3 by Abel Deuring
schema modifications by stub
140
    format = EnumCol(enum=HWSubmissionFormat, notNull=True)
4675.1.4 by Abel Deuring
implemented reviewer's comments
141
    status = EnumCol(enum=HWSubmissionProcessingStatus, notNull=True)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
142
    private = BoolCol(notNull=True)
143
    contactable = BoolCol(notNull=True)
4675.1.6 by Abel Deuring
dropped hwsubmission.live_cd; renamed hwsubmission.submisson_id to hwsubmission.submission_key
144
    submission_key = StringCol(notNull=True)
5852.2.2 by James Henstridge
fix some columns that claimed to be notNull=True but are in fact not not null
145
    owner = ForeignKey(dbName='owner', foreignKey='Person',
5821.2.40 by James Henstridge
* Move all the uses of public_person_validator over to the Storm
146
                       storm_validator=validate_public_person)
5852.2.2 by James Henstridge
fix some columns that claimed to be notNull=True but are in fact not not null
147
    distroarchseries = ForeignKey(dbName='distroarchseries',
5821.2.65 by James Henstridge
* HWSubmission.distroarchseries can be None.
148
                                  foreignKey='DistroArchSeries')
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
149
    raw_submission = ForeignKey(dbName='raw_submission',
4675.1.4 by Abel Deuring
implemented reviewer's comments
150
                                foreignKey='LibraryFileAlias',
5852.2.2 by James Henstridge
fix some columns that claimed to be notNull=True but are in fact not not null
151
                                notNull=False, default=DEFAULT)
4675.1.2 by Abel Deuring
changed column names in HWDBSumissions; added two indexes for HWDBSumission
152
    system_fingerprint = ForeignKey(dbName='system_fingerprint',
4675.1.4 by Abel Deuring
implemented reviewer's comments
153
                                    foreignKey='HWSystemFingerprint',
154
                                    notNull=True)
6740.2.1 by Abel Deuring
fix for bug 250589: OOPS rendering +hwdb-submissions page
155
    raw_emailaddress = StringCol()
4675.1.3 by Abel Deuring
schema modifications by stub
156
7525.3.1 by Abel Deuring
Webservice API for IHWSubmissionDevice
157
    @property
158
    def devices(self):
159
        return HWSubmissionDeviceSet().getDevices(submission=self)
160
4675.1.3 by Abel Deuring
schema modifications by stub
161
162
class HWSubmissionSet:
163
    """See `IHWSubmissionSet`."""
164
165
    implements(IHWSubmissionSet)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
166
167
    def createSubmission(self, date_created, format, private, contactable,
4675.1.6 by Abel Deuring
dropped hwsubmission.live_cd; renamed hwsubmission.submisson_id to hwsubmission.submission_key
168
                         submission_key, emailaddress, distroarchseries,
169
                         raw_submission, filename, filesize,
170
                         system_fingerprint):
4675.1.3 by Abel Deuring
schema modifications by stub
171
        """See `IHWSubmissionSet`."""
4912.1.3 by Christian Reis
Implement the plain-text view for HWDB submissions, check that submission IDs are really valid_names, rename submission_id to submission_key consistently, rename distro*release to distro*series consistently. What else..
172
        assert valid_name(submission_key), "Invalid key %s" % submission_key
173
4675.1.4 by Abel Deuring
implemented reviewer's comments
174
        submission_exists = HWSubmission.selectOneBy(
4675.1.6 by Abel Deuring
dropped hwsubmission.live_cd; renamed hwsubmission.submisson_id to hwsubmission.submission_key
175
            submission_key=submission_key)
4675.1.4 by Abel Deuring
implemented reviewer's comments
176
        if submission_exists is not None:
4675.1.5 by Abel Deuring
improved HWDB docs; a new exception for HWDB submissions
177
            raise HWSubmissionKeyNotUnique(
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
178
                'A submission with this ID already exists')
4912.1.3 by Christian Reis
Implement the plain-text view for HWDB submissions, check that submission IDs are really valid_names, rename submission_id to submission_key consistently, rename distro*release to distro*series consistently. What else..
179
4675.1.3 by Abel Deuring
schema modifications by stub
180
        personset = getUtility(IPersonSet)
6740.2.1 by Abel Deuring
fix for bug 250589: OOPS rendering +hwdb-submissions page
181
        if emailaddress is not None:
182
            owner = personset.getByEmail(emailaddress)
183
        else:
184
            owner = None
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
185
4675.1.3 by Abel Deuring
schema modifications by stub
186
        fingerprint = HWSystemFingerprint.selectOneBy(
4675.1.2 by Abel Deuring
changed column names in HWDBSumissions; added two indexes for HWDBSumission
187
            fingerprint=system_fingerprint)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
188
        if fingerprint is None:
4675.4.1 by Christian Reis
Fix some broken merges, update test to properly use sampledata, rename HWDBSubmission* to HWSubmission.
189
            fingerprint = HWSystemFingerprint(fingerprint=system_fingerprint)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
190
191
        libraryfileset = getUtility(ILibraryFileAliasSet)
192
        libraryfile = libraryfileset.create(
193
            name=filename,
194
            size=filesize,
195
            file=raw_submission,
6916.1.1 by Curtis Hovey
Fixed comment formats to fidn missing persons, dates, and some bugs.
196
            # XXX: kiko 2007-09-20: The hwdb client sends us bzipped XML, but
197
            # arguably other clients could send us other formats. The right
198
            # way to do this is either to enforce the format in the browser
4912.1.6 by Christian Reis
Order submissions in reverse date order, and change mime-type of file to bzip2.
199
            # code, allow the client to specify the format, or use a
200
            # magic module to sniff what it is we got.
201
            contentType='application/x-bzip2',
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
202
            expires=None)
203
4675.1.3 by Abel Deuring
schema modifications by stub
204
        return HWSubmission(
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
205
            date_created=date_created,
206
            format=format,
4675.1.4 by Abel Deuring
implemented reviewer's comments
207
            status=HWSubmissionProcessingStatus.SUBMITTED,
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
208
            private=private,
209
            contactable=contactable,
4675.1.6 by Abel Deuring
dropped hwsubmission.live_cd; renamed hwsubmission.submisson_id to hwsubmission.submission_key
210
            submission_key=submission_key,
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
211
            owner=owner,
4912.1.3 by Christian Reis
Implement the plain-text view for HWDB submissions, check that submission IDs are really valid_names, rename submission_id to submission_key consistently, rename distro*release to distro*series consistently. What else..
212
            distroarchseries=distroarchseries,
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
213
            raw_submission=libraryfile,
4974.3.2 by Abel Deuring
No automatic creation of Person records for HWDB from unknown email addresses
214
            system_fingerprint=fingerprint,
215
            raw_emailaddress=emailaddress)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
216
4675.5.1 by Abel Deuring
allow team ownership of HWDB submissions
217
    def _userHasAccessClause(self, user):
5485.1.18 by Edwin Grubbs
Fixed lots of lint issues
218
        """Limit results of HWSubmission queries to rows the user can access.
219
        """
4675.5.1 by Abel Deuring
allow team ownership of HWDB submissions
220
        admins = getUtility(ILaunchpadCelebrities).admin
221
        if user is None:
222
            return " AND NOT HWSubmission.private"
223
        elif not user.inTeam(admins):
224
            return """
225
                AND (NOT HWSubmission.private
226
                     OR EXISTS
227
                         (SELECT 1
228
                             FROM HWSubmission as HWAccess, TeamParticipation
229
                             WHERE HWAccess.id=HWSubmission.id
8630.1.1 by Abel Deuring
Fix performance issues of deviceDriverOwnersAffectedByBugs when called with a given driver but without a device.
230
                                 AND HWAccess.private
4675.5.1 by Abel Deuring
allow team ownership of HWDB submissions
231
                                 AND HWAccess.owner=TeamParticipation.team
232
                                 AND TeamParticipation.person=%i
233
                                 ))
234
                """ % user.id
235
        else:
236
            return ""
237
4912.1.3 by Christian Reis
Implement the plain-text view for HWDB submissions, check that submission IDs are really valid_names, rename submission_id to submission_key consistently, rename distro*release to distro*series consistently. What else..
238
    def getBySubmissionKey(self, submission_key, user=None):
4675.1.3 by Abel Deuring
schema modifications by stub
239
        """See `IHWSubmissionSet`."""
6978.2.2 by Abel Deuring
implemented reviewer's comments
240
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
241
        return store.find(
242
            HWSubmission,
243
            And(HWSubmission.submission_key == submission_key,
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
244
                _userCanAccessSubmissionStormClause(user))).one()
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
245
246
    def getByFingerprintName(self, name, user=None):
4675.1.3 by Abel Deuring
schema modifications by stub
247
        """See `IHWSubmissionSet`."""
248
        fp = HWSystemFingerprintSet().getByName(name)
4675.1.4 by Abel Deuring
implemented reviewer's comments
249
        query = """
250
            system_fingerprint=%s
251
            AND HWSystemFingerprint.id = HWSubmission.system_fingerprint
252
            """ % sqlvalues(fp)
4675.5.1 by Abel Deuring
allow team ownership of HWDB submissions
253
        query = query + self._userHasAccessClause(user)
254
4675.1.3 by Abel Deuring
schema modifications by stub
255
        return HWSubmission.select(
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
256
            query,
4675.1.3 by Abel Deuring
schema modifications by stub
257
            clauseTables=['HWSystemFingerprint'],
258
            prejoinClauseTables=['HWSystemFingerprint'],
4912.1.6 by Christian Reis
Order submissions in reverse date order, and change mime-type of file to bzip2.
259
            orderBy=['-date_submitted',
260
                     'HWSystemFingerprint.fingerprint',
4675.1.6 by Abel Deuring
dropped hwsubmission.live_cd; renamed hwsubmission.submisson_id to hwsubmission.submission_key
261
                     'submission_key'])
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
262
263
    def getByOwner(self, owner, user=None):
4675.1.3 by Abel Deuring
schema modifications by stub
264
        """See `IHWSubmissionSet`."""
4675.1.4 by Abel Deuring
implemented reviewer's comments
265
        query = """
266
            owner=%i
267
            AND HWSystemFingerprint.id = HWSubmission.system_fingerprint
268
            """ % owner.id
4675.5.1 by Abel Deuring
allow team ownership of HWDB submissions
269
        query = query + self._userHasAccessClause(user)
4675.3.2 by Abel Deuring
added views for hwdb submissiions from a person and submissions for a fingerprint
270
4675.1.3 by Abel Deuring
schema modifications by stub
271
        return HWSubmission.select(
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
272
            query,
4675.1.3 by Abel Deuring
schema modifications by stub
273
            clauseTables=['HWSystemFingerprint'],
274
            prejoinClauseTables=['HWSystemFingerprint'],
4912.1.6 by Christian Reis
Order submissions in reverse date order, and change mime-type of file to bzip2.
275
            orderBy=['-date_submitted',
276
                     'HWSystemFingerprint.fingerprint',
4675.1.6 by Abel Deuring
dropped hwsubmission.live_cd; renamed hwsubmission.submisson_id to hwsubmission.submission_key
277
                     'submission_key'])
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
278
4675.4.1 by Christian Reis
Fix some broken merges, update test to properly use sampledata, rename HWDBSubmission* to HWSubmission.
279
    def submissionIdExists(self, submission_key):
4974.3.2 by Abel Deuring
No automatic creation of Person records for HWDB from unknown email addresses
280
        """See `IHWSubmissionSet`."""
281
        rows = HWSubmission.selectBy(submission_key=submission_key)
282
        return rows.count() > 0
283
6978.2.1 by Abel Deuring
retrieve HWDB submissions by processing status
284
    def getByStatus(self, status, user=None):
285
        """See `IHWSubmissionSet`."""
286
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
287
        result_set = store.find(HWSubmission,
288
                                HWSubmission.status == status,
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
289
                                _userCanAccessSubmissionStormClause(user))
7554.1.2 by Bjorn Tillenius
don't sort by date_submitted, since there isn't an index for it.
290
        # Provide a stable order. Sorting by id, to get the oldest
291
        # submissions first. When date_submitted has an index, we could
292
        # sort by that first.
293
        result_set.order_by(HWSubmission.id)
6978.2.1 by Abel Deuring
retrieve HWDB submissions by processing status
294
        return result_set
295
7465.7.1 by Abel Deuring
search method for IHWSubmissionSet
296
    def search(self, user=None, device=None, driver=None, distribution=None,
13165.3.2 by Marc Tardif
Extended the search method from IHWSubmissionSet to filter before date created, after date created, before date submitted and after date submitted.
297
               distroseries=None, architecture=None, owner=None,
298
               created_before=None, created_after=None,
299
               submitted_before=None, submitted_after=None):
7465.7.1 by Abel Deuring
search method for IHWSubmissionSet
300
        """See `IHWSubmissionSet`."""
301
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
302
        args = []
303
        if device is not None:
304
            args.append(HWDeviceDriverLink.device == HWDevice.id)
305
            args.append(HWDevice.id == device.id)
306
        if driver is not None:
307
            args.append(HWDeviceDriverLink.driver == HWDriver.id)
308
            args.append(HWDriver.id == driver.id)
309
        # HWDevice and HWDriver are linked to submissions via
310
        # HWDeviceDriverLink and HWSubmissionDevice.
311
        if args:
312
            args.append(HWSubmissionDevice.device_driver_link ==
313
                        HWDeviceDriverLink.id)
314
            args.append(HWSubmissionDevice.submission == HWSubmission.id)
315
7734.1.4 by Tom Berger
de-lint
316
        if (distribution is not None or distroseries is not None
317
            or architecture is not None):
318
            # We need to select a specific distribution, distroseries,
319
            # and/or processor architecture.
7734.1.5 by Tom Berger
write the assertion correctly
320
            if distribution and distroseries:
7734.1.6 by Tom Berger
some review suggestions from mwhudson - test the assertion, and correct some nonsensical narrative in doctests
321
                raise IllegalQuery(
7734.1.5 by Tom Berger
write the assertion correctly
322
                    'Only one of `distribution` or '
323
                    '`distroseries` can be present.')
7465.7.1 by Abel Deuring
search method for IHWSubmissionSet
324
            args.append(HWSubmission.distroarchseries == DistroArchSeries.id)
325
            if architecture is not None:
326
                args.append(DistroArchSeries.architecturetag == architecture)
327
            if distribution is not None:
328
                args.append(DistroArchSeries.distroseries == DistroSeries.id)
329
                args.append(DistroSeries.distribution == Distribution.id)
330
                args.append(Distribution.id == distribution.id)
7734.1.1 by Tom Berger
allow searching submissions by distroseries + doctests
331
            if distroseries is not None:
332
                args.append(DistroArchSeries.distroseries == distroseries.id)
7746.1.1 by Tom Berger
make it possible to filter submissions by owner
333
        if owner is not None:
334
            args.append(HWSubmission.owner == owner.id)
13165.3.2 by Marc Tardif
Extended the search method from IHWSubmissionSet to filter before date created, after date created, before date submitted and after date submitted.
335
        if created_before is not None:
336
            args.append(HWSubmission.date_created <= created_before)
337
        if created_after is not None:
338
            args.append(HWSubmission.date_created > created_after)
339
        if submitted_before is not None:
340
            args.append(HWSubmission.date_submitted <= submitted_before)
341
        if submitted_after is not None:
342
            args.append(HWSubmission.date_submitted > submitted_after)
343
7465.7.1 by Abel Deuring
search method for IHWSubmissionSet
344
        result_set = store.find(
345
            HWSubmission,
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
346
            _userCanAccessSubmissionStormClause(user),
7465.7.1 by Abel Deuring
search method for IHWSubmissionSet
347
            *args)
348
        # Many devices are associated with more than one driver, even
349
        # for one submission, hence we may have more than one
350
        # HWSubmissionDevice record and more than one HWDeviceDriverLink
351
        # for one device and one submission matching the WHERE clause
352
        # defined above. This leads to duplicate results without a
353
        # DISTINCT clause.
354
        result_set.config(distinct=True)
355
        result_set.order_by(HWSubmission.id)
11411.2.1 by Michael Nelson
Removed workarounds for bug 217644.
356
        return result_set
7465.7.1 by Abel Deuring
search method for IHWSubmissionSet
357
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
358
    def _submissionsSubmitterSelects(
359
        self, target_column, bus, vendor_id, product_id, driver_name,
360
        package_name, distro_target):
361
        """Return Select objects for statistical queries.
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
362
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
363
        :return: A tuple
364
            (select_device_related_records, select_all_records)
365
            where select_device_related_records is a Select instance
366
            returning target_column matching all other method
367
            parameters, and where select_all_records is a Select
368
            instance returning target_column and matching distro_target,
369
        :param target_column: The records returned by the Select instance.
370
        :param bus: The `HWBus` of the device.
371
        :param vendor_id: The vendor ID of the device.
372
        :param product_id: The product ID of the device.
373
        :param driver_name: The name of the driver used for the device
374
            (optional).
375
        :param package_name: The name of the package the driver is a part of.
376
            (optional).
377
        :param distro_target: Limit the result to submissions made for the
378
            given distribution, distroseries or distroarchseries.
379
            (optional).
380
        """
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
381
        tables, clauses = make_distro_target_clause(distro_target)
382
        if HWSubmission not in tables:
383
            tables.append(HWSubmission)
384
        clauses.append(
385
            HWSubmission.status == HWSubmissionProcessingStatus.PROCESSED)
386
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
387
        all_submissions = Select(
388
            columns=[target_column], tables=tables, where=And(*clauses),
389
            distinct=True)
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
390
391
        device_tables, device_clauses = (
392
            make_submission_device_statistics_clause(
8697.19.1 by Abel Deuring
allow driver-only queries for HWDB statistics methods.
393
                bus, vendor_id, product_id, driver_name, package_name, False))
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
394
        submission_ids = Select(
395
            columns=[HWSubmissionDevice.submissionID],
396
            tables=device_tables, where=And(*device_clauses))
397
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
398
        clauses.append(HWSubmission.id.is_in(submission_ids))
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
399
        submissions_with_device = Select(
400
            columns=[target_column], tables=tables, where=And(*clauses),
401
            distinct=True)
402
403
        return (submissions_with_device, all_submissions)
404
405
    def numSubmissionsWithDevice(
8697.19.1 by Abel Deuring
allow driver-only queries for HWDB statistics methods.
406
        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
407
        package_name=None, distro_target=None):
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
408
        """See `IHWSubmissionSet`."""
409
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
410
        submissions_with_device_select, all_submissions_select = (
411
            self._submissionsSubmitterSelects(
412
                Count(), bus, vendor_id, product_id, driver_name,
413
                package_name, distro_target))
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
414
        submissions_with_device = store.execute(
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
415
            submissions_with_device_select)
416
        all_submissions = store.execute(all_submissions_select)
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
417
        return (submissions_with_device.get_one()[0],
418
                all_submissions.get_one()[0])
419
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
420
    def numOwnersOfDevice(
8697.19.1 by Abel Deuring
allow driver-only queries for HWDB statistics methods.
421
        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
422
        package_name=None, distro_target=None):
8366.1.1 by Abel Deuring
HWSubmission.numDeviceOwners() added, counting the the number of device owners.
423
        """See `IHWSubmissionSet`."""
424
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
425
        submitters_with_device_select, all_submitters_select = (
426
            self._submissionsSubmitterSelects(
427
                HWSubmission.raw_emailaddress, bus, vendor_id, product_id,
428
                driver_name, package_name, distro_target))
429
430
        submitters_with_device = store.execute(
431
            Select(
432
                columns=[Count()],
433
                tables=[Alias(submitters_with_device_select, 'addresses')]))
434
        all_submitters = store.execute(
435
            Select(
436
                columns=[Count()],
437
                tables=[Alias(all_submitters_select, 'addresses')]))
438
439
        return (submitters_with_device.get_one()[0],
440
                all_submitters.get_one()[0])
441
8451.2.2 by Abel Deuring
allow to call deviceDriverOwnersAffectedByBugs only with a driver
442
    def deviceDriverOwnersAffectedByBugs(
443
        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
444
        package_name=None, bug_ids=None, bug_tags=None, affected_by_bug=False,
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
445
        subscribed_to_bug=False, user=None):
446
        """See `IHWSubmissionSet`."""
447
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
448
        tables, clauses = make_submission_device_statistics_clause(
8451.2.2 by Abel Deuring
allow to call deviceDriverOwnersAffectedByBugs only with a driver
449
                bus, vendor_id, product_id, driver_name, package_name, False)
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
450
        tables.append(HWSubmission)
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
451
        clauses.append(HWSubmissionDevice.submission == HWSubmission.id)
8486.18.5 by Abel Deuring
exposed bug.linkHWSubmission()m bug.unlinkHWSubmission and bug.getHWSubmissions() to the webservice API.
452
        clauses.append(_userCanAccessSubmissionStormClause(user))
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
453
8451.4.5 by Deryck Hodge
Update an older method to use And for bug_ids and bug_tags
454
        if ((bug_ids is None or len(bug_ids) == 0) and
455
            (bug_tags is None or len(bug_tags) == 0)):
456
            raise ParameterError('bug_ids or bug_tags must be supplied.')
457
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
458
        tables.append(Bug)
8451.4.5 by Deryck Hodge
Update an older method to use And for bug_ids and bug_tags
459
        if bug_ids is not None and bug_ids is not []:
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
460
            clauses.append(Bug.id.is_in(bug_ids))
8451.4.5 by Deryck Hodge
Update an older method to use And for bug_ids and bug_tags
461
462
        if bug_tags is not None and bug_tags is not []:
463
            clauses.extend([
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
464
                Bug.id == BugTag.bugID, BugTag.tag.is_in(bug_tags)])
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
465
            tables.append(BugTag)
466
467
        # If we OR-combine the search for bug owners, subscribers
468
        # and affected people on SQL level, the query runs very slow.
469
        # So let's run the queries separately and join the results
470
        # on Python level.
471
7675.166.310 by Stuart Bishop
DB Unicode fixes
472
        # This would be quicker still if we did it as a single query
473
        # using UNION.
474
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
475
        owner_query = Select(
476
            columns=[HWSubmission.ownerID], tables=tables,
477
            where=And(*(clauses + [Bug.ownerID == HWSubmission.ownerID])))
478
        user_ids = set(store.execute(owner_query))
479
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
480
        if subscribed_to_bug:
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
481
            subscriber_clauses = [
11536.1.2 by Gavin Panella
Fix many tests, and a bug or two.
482
                BugSubscription.person_id == HWSubmission.ownerID,
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
483
                BugSubscription.bug == Bug.id,
484
                ]
485
            subscriber_query = Select(
486
                columns=[HWSubmission.ownerID],
487
                tables=tables + [BugSubscription],
488
                where=And(*(clauses + subscriber_clauses)))
489
            user_ids.update(store.execute(subscriber_query))
490
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
491
        if affected_by_bug:
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
492
            affected_clauses = [
493
                BugAffectsPerson.personID == HWSubmission.ownerID,
494
                BugAffectsPerson.bug == Bug.id,
495
                BugAffectsPerson.affected,
496
                ]
497
            affected_query = Select(
498
                columns=[HWSubmission.ownerID],
499
                tables=tables + [BugAffectsPerson],
500
                where=And(*(clauses + affected_clauses)))
501
            user_ids.update(store.execute(affected_query))
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
502
8520.8.1 by Abel Deuring
changed the SQL queries in HWDBSubmissionSet.deviceDriverOwnersAffectedByBugs() to imprve performance
503
        # A "WHERE x IN (y, z...)" query needs at least one element
504
        # on the right side of IN.
505
        if len(user_ids) == 0:
506
            result = store.find(Person, False)
507
        else:
7675.166.310 by Stuart Bishop
DB Unicode fixes
508
            user_ids = [row[0] for row in user_ids]
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
509
            result = store.find(Person, Person.id.is_in(user_ids))
8451.2.1 by Abel Deuring
Method added to retrieve a list of device owners who are affected by bugs
510
        result.order_by(Person.displayname)
511
        return result
512
8451.4.2 by Deryck Hodge
Change the method name.
513
    def hwInfoByBugRelatedUsers(
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
514
        self, bug_ids=None, bug_tags=None, affected_by_bug=False,
515
        subscribed_to_bug=False, user=None):
516
        """See `IHWSubmissionSet`."""
517
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
518
8451.4.5 by Deryck Hodge
Update an older method to use And for bug_ids and bug_tags
519
        if ((bug_ids is None or len(bug_ids) == 0) and
520
            (bug_tags is None or len(bug_tags) == 0)):
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
521
            raise ParameterError('bug_ids or bug_tags must be supplied.')
522
523
        tables = [
524
            Person, HWSubmission, HWSubmissionDevice, HWDeviceDriverLink,
525
            HWDevice, HWVendorID, Bug, BugTag,
526
            ]
527
528
        clauses = [
529
            Person.id == HWSubmission.ownerID,
530
            HWSubmissionDevice.submission == HWSubmission.id,
531
            HWSubmissionDevice.device_driver_link == HWDeviceDriverLink.id,
532
            HWDeviceDriverLink.device == HWDevice.id,
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
533
            HWDevice.bus_vendor == HWVendorID.id]
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
534
8451.4.3 by Deryck Hodge
Change handling of bug_ids and bug_tags to be an And query.
535
        if bug_ids is not None and bug_ids is not []:
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
536
            clauses.append(Bug.id.is_in(bug_ids))
8451.4.3 by Deryck Hodge
Change handling of bug_ids and bug_tags to be an And query.
537
538
        if bug_tags is not None and bug_tags is not []:
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
539
            clauses.extend(
540
                [Bug.id == BugTag.bugID, BugTag.tag.is_in(bug_tags)])
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
541
8486.18.9 by Abel Deuring
fixed a test failure
542
        clauses.append(_userCanAccessSubmissionStormClause(user))
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
543
544
        person_clauses = [Bug.ownerID == HWSubmission.ownerID]
545
        if subscribed_to_bug:
546
            person_clauses.append(
11536.1.2 by Gavin Panella
Fix many tests, and a bug or two.
547
                And(BugSubscription.person_id == HWSubmission.ownerID,
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
548
                    BugSubscription.bug == Bug.id))
549
            tables.append(BugSubscription)
550
        if affected_by_bug:
551
            person_clauses.append(
552
                And(BugAffectsPerson.personID == HWSubmission.ownerID,
553
                    BugAffectsPerson.bug == Bug.id,
554
                    BugAffectsPerson.affected))
555
            tables.append(BugAffectsPerson)
556
        clauses.append(Or(person_clauses))
557
558
        query = Select(
559
            columns=[
560
                Person.name, HWVendorID.bus,
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
561
                HWVendorID.vendor_id_for_bus, HWDevice.bus_product_id],
8451.4.1 by Deryck Hodge
Enabling deviceDriversByOwner for HWSubmissionSet and the
562
            tables=tables, where=And(*clauses), distinct=True,
563
            order_by=[HWVendorID.bus, HWVendorID.vendor_id_for_bus,
564
                      HWDevice.bus_product_id, Person.name])
565
566
        return [
567
            (person_name, HWBus.items[bus_id], vendor_id, product_id)
568
             for person_name, bus_id, vendor_id, product_id
569
             in store.execute(query)]
570
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
571
4675.1.3 by Abel Deuring
schema modifications by stub
572
class HWSystemFingerprint(SQLBase):
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
573
    """Identifiers of a computer system."""
574
4675.1.3 by Abel Deuring
schema modifications by stub
575
    implements(IHWSystemFingerprint)
6130.3.5 by Abel Deuring
implemented reviewer's comments
576
4675.1.3 by Abel Deuring
schema modifications by stub
577
    _table = 'HWSystemFingerprint'
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
578
579
    fingerprint = StringCol(notNull=True)
580
581
4675.1.3 by Abel Deuring
schema modifications by stub
582
class HWSystemFingerprintSet:
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
583
    """A set of identifiers of a computer system."""
584
4675.1.3 by Abel Deuring
schema modifications by stub
585
    implements(IHWSystemFingerprintSet)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
586
587
    def getByName(self, fingerprint):
4675.1.3 by Abel Deuring
schema modifications by stub
588
        """See `IHWSystemFingerprintSet`."""
589
        return HWSystemFingerprint.selectOneBy(fingerprint=fingerprint)
4675.1.1 by Abel Deuring
initial data structures for HWDB submissions
590
591
    def createFingerprint(self, fingerprint):
4675.1.3 by Abel Deuring
schema modifications by stub
592
        """See `IHWSystemFingerprintSet`."""
593
        return HWSystemFingerprint(fingerprint=fingerprint)
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
594
595
596
class HWVendorName(SQLBase):
597
    """See `IHWVendorName`."""
598
599
    implements(IHWVendorName)
6130.3.5 by Abel Deuring
implemented reviewer's comments
600
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
601
    _table = 'HWVendorName'
602
603
    name = StringCol(notNull=True)
604
605
606
class HWVendorNameSet:
607
    """See `IHWVendorNameSet`."""
608
609
    implements(IHWVendorNameSet)
6130.3.5 by Abel Deuring
implemented reviewer's comments
610
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
611
    def create(self, name):
612
        """See `IHWVendorNameSet`."""
613
        return HWVendorName(name=name)
614
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
615
    def getByName(self, name):
616
        """See `IHWVendorNameSet`."""
6440.4.1 by Abel Deuring
case-insensitive uniqueness constraint for names in HWVendorName; case-insensitive lookup of names in HWVendorName
617
        return HWVendorName.selectOne(
618
            'ulower(name)=ulower(%s)' % sqlvalues(name))
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
619
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
620
621
four_hex_digits = re.compile('^0x[0-9a-f]{4}$')
622
six_hex_digits = re.compile('^0x[0-9a-f]{6}$')
623
# The regular expressions for the SCSI vendor and product IDs are not as
624
# "picky" as the specification requires. Considering the fact that for
625
# example Microtek sold at least one scanner model that returns '        '
626
# as the vendor ID, it seems reasonable to allows also somewhat broken
627
# looking IDs.
628
scsi_vendor = re.compile('^.{8}$')
629
scsi_product = re.compile('^.{16}$')
630
6130.3.3 by Abel Deuring
implemented reviewer's comments
631
validVendorID = {
632
    HWBus.PCI: four_hex_digits,
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
633
    HWBus.PCCARD: four_hex_digits,
6130.3.3 by Abel Deuring
implemented reviewer's comments
634
    HWBus.USB: four_hex_digits,
635
    HWBus.IEEE1394: six_hex_digits,
636
    HWBus.SCSI: scsi_vendor,
637
    }
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
638
6130.3.3 by Abel Deuring
implemented reviewer's comments
639
validProductID = {
640
    HWBus.PCI: four_hex_digits,
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
641
    HWBus.PCCARD: four_hex_digits,
6130.3.3 by Abel Deuring
implemented reviewer's comments
642
    HWBus.USB: four_hex_digits,
643
    HWBus.SCSI: scsi_product,
644
    }
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
645
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
646
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
647
def isValidVendorID(bus, id):
6130.3.5 by Abel Deuring
implemented reviewer's comments
648
    """Check that the string id is a valid vendor ID for this bus.
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
649
650
    :return: True, if id is valid, otherwise False
6130.3.4 by Abel Deuring
implemented reviewer's comments
651
    :param bus: A `HWBus` indicating the bus type of "id"
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
652
    :param id: A string with the ID
653
6130.3.3 by Abel Deuring
implemented reviewer's comments
654
    Some busses have constraints for IDs, while some can use arbitrary
655
    values, for example the "fake" busses HWBus.SYSTEM and HWBus.SERIAL.
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
656
657
    We use a hexadecimal representation of integers like "0x123abc",
658
    i.e., the numbers have the prefix "0x"; for the digits > 9 we
6130.3.3 by Abel Deuring
implemented reviewer's comments
659
    use the lower case characters a to f.
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
660
661
    USB and PCI IDs have always four digits; IEEE1394 IDs have always
662
    six digits.
663
664
    SCSI vendor IDs consist of eight bytes of ASCII data (0x20..0x7e);
6130.3.3 by Abel Deuring
implemented reviewer's comments
665
    if a vendor name has less than eight characters, it is padded on the
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
666
    right with spaces (See http://t10.org/ftp/t10/drafts/spc4/spc4r14.pdf,
667
    page 45).
668
    """
669
    if bus not in validVendorID:
670
        return True
671
    return validVendorID[bus].search(id) is not None
672
673
674
def isValidProductID(bus, id):
6130.3.5 by Abel Deuring
implemented reviewer's comments
675
    """Check that the string id is a valid product for this bus.
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
676
677
    :return: True, if id is valid, otherwise False
6130.3.4 by Abel Deuring
implemented reviewer's comments
678
    :param bus: A `HWBus` indicating the bus type of "id"
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
679
    :param id: A string with the ID
680
6130.3.3 by Abel Deuring
implemented reviewer's comments
681
    Some busses have constraints for IDs, while some can use arbitrary
682
    values, for example the "fake" busses HWBus.SYSTEM and HWBus.SERIAL.
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
683
684
    We use a hexadecimal representation of integers like "0x123abc",
685
    i.e., the numbers have the prefix "0x"; for the digits > 9 we
6130.3.3 by Abel Deuring
implemented reviewer's comments
686
    use the lower case characters a to f.
687
688
    USB and PCI IDs have always four digits.
689
690
    Since IEEE1394 does not specify product IDs, there is no formal
691
    check of them.
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
692
693
    SCSI product IDs consist of 16 bytes of ASCII data (0x20..0x7e);
6130.3.3 by Abel Deuring
implemented reviewer's comments
694
    if a product name has less than 16 characters, it is padded on the
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
695
    right with spaces.
696
    """
697
    if bus not in validProductID:
698
        return True
699
    return validProductID[bus].search(id) is not None
700
701
702
class HWVendorID(SQLBase):
703
    """See `IHWVendorID`."""
704
705
    implements(IHWVendorID)
6130.3.5 by Abel Deuring
implemented reviewer's comments
706
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
707
    _table = 'HWVendorID'
708
709
    bus = EnumCol(enum=HWBus, notNull=True)
710
    vendor_id_for_bus = StringCol(notNull=True)
711
    vendor_name = ForeignKey(dbName='vendor_name', foreignKey='HWVendorName',
712
                             notNull=True)
713
6130.3.4 by Abel Deuring
implemented reviewer's comments
714
    def _create(self, id, **kw):
6130.3.5 by Abel Deuring
implemented reviewer's comments
715
        bus = kw.get('bus')
716
        if bus is None:
717
            raise TypeError('HWVendorID() did not get expected keyword '
718
                            'argument bus')
719
        vendor_id_for_bus = kw.get('vendor_id_for_bus')
720
        if vendor_id_for_bus is None:
721
            raise TypeError('HWVendorID() did not get expected keyword '
722
                            'argument vendor_id_for_bus')
723
        if not isValidVendorID(bus, vendor_id_for_bus):
8063.1.1 by Abel Deuring
better error handling for webservice API queries for HWDB devices with invalid vendor or product IDs.
724
            raise ParameterError(
725
                '%s is not a valid vendor ID for %s'
726
                % (repr(vendor_id_for_bus), bus.title))
6130.3.4 by Abel Deuring
implemented reviewer's comments
727
        SQLBase._create(self, id, **kw)
6130.3.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 1
728
729
730
class HWVendorIDSet:
731
    """See `IHWVendorIDSet`."""
732
733
    implements(IHWVendorIDSet)
734
735
    def create(self, bus, vendor_id, vendor_name):
736
        """See `IHWVendorIDSet`."""
737
        vendor_name = HWVendorName.selectOneBy(name=vendor_name.name)
738
        return HWVendorID(bus=bus, vendor_id_for_bus=vendor_id,
739
                          vendor_name=vendor_name)
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
740
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
741
    def getByBusAndVendorID(self, bus, vendor_id):
742
        """See `IHWVendorIDSet`."""
743
        if not isValidVendorID(bus, vendor_id):
8063.1.1 by Abel Deuring
better error handling for webservice API queries for HWDB devices with invalid vendor or product IDs.
744
            raise ParameterError(
745
                '%s is not a valid vendor ID for %s'
746
                % (repr(vendor_id), bus.title))
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
747
        return HWVendorID.selectOneBy(bus=bus, vendor_id_for_bus=vendor_id)
748
7553.1.1 by Abel Deuring
webservice API for IHWVendorID
749
    def get(self, id):
750
        """See `IHWVendorIDSet`."""
751
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
752
        return store.find(HWVendorID, HWVendorID.id == id).one()
753
754
    def idsForBus(self, bus):
755
        """See `IHWVendorIDSet`."""
756
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
757
        result_set = store.find(HWVendorID, bus=bus)
758
        result_set.order_by(HWVendorID.vendor_id_for_bus)
759
        return result_set
760
6130.11.1 by Abel Deuring
implemented reviewer's comments
761
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
762
class HWDevice(SQLBase):
763
    """See `IHWDevice.`"""
764
765
    implements(IHWDevice)
766
    _table = 'HWDevice'
767
6130.11.2 by Abel Deuring
RF merge
768
    # XXX Abel Deuring 2008-05-02: The columns bus_vendor and
769
    # bus_product_id are supposed to be immutable. However, if they
770
    # are defined as "immutable=True", the creation of a new HWDevice
771
    # instance leads to an AttributeError in sqlobject/main.py, line 814.
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
772
    bus_vendor = ForeignKey(dbName='bus_vendor_id', foreignKey='HWVendorID',
6130.11.2 by Abel Deuring
RF merge
773
                            notNull=True, immutable=False)
6130.11.1 by Abel Deuring
implemented reviewer's comments
774
    bus_product_id = StringCol(notNull=True, dbName='bus_product_id',
6130.11.2 by Abel Deuring
RF merge
775
                               immutable=False)
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
776
    variant = StringCol(notNull=False)
777
    name = StringCol(notNull=True)
778
    submissions = IntCol(notNull=True)
779
7353.2.1 by Abel Deuring
Webservice API for IHWDevice
780
    @property
781
    def bus(self):
782
        return self.bus_vendor.bus
783
784
    @property
785
    def vendor_id(self):
786
        return self.bus_vendor.vendor_id_for_bus
787
788
    @property
789
    def vendor_name(self):
790
        return self.bus_vendor.vendor_name.name
791
6130.11.2 by Abel Deuring
RF merge
792
    def _create(self, id, **kw):
793
        bus_vendor = kw.get('bus_vendor')
794
        if bus_vendor is None:
795
            raise TypeError('HWDevice() did not get expected keyword '
796
                            'argument bus_vendor')
797
        bus_product_id = kw.get('bus_product_id')
798
        if bus_product_id is None:
799
            raise TypeError('HWDevice() did not get expected keyword '
800
                            'argument bus_product_id')
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
801
        if not isValidProductID(bus_vendor.bus, bus_product_id):
8063.1.1 by Abel Deuring
better error handling for webservice API queries for HWDB devices with invalid vendor or product IDs.
802
            raise ParameterError(
803
                '%s is not a valid product ID for %s'
804
                % (repr(bus_product_id), bus_vendor.bus.title))
6130.11.2 by Abel Deuring
RF merge
805
        SQLBase._create(self, id, **kw)
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
806
7525.2.3 by Abel Deuring
Webservice API for HWDB submissions
807
    def getSubmissions(self, driver=None, distribution=None,
7746.1.1 by Tom Berger
make it possible to filter submissions by owner
808
                       distroseries=None, architecture=None, owner=None):
7525.2.3 by Abel Deuring
Webservice API for HWDB submissions
809
        """See `IHWDevice.`"""
810
        return HWSubmissionSet().search(
7734.1.1 by Tom Berger
allow searching submissions by distroseries + doctests
811
            device=self, driver=driver, distribution=distribution,
7746.1.1 by Tom Berger
make it possible to filter submissions by owner
812
            distroseries=distroseries, architecture=architecture, owner=owner)
7525.2.3 by Abel Deuring
Webservice API for HWDB submissions
813
7604.2.1 by Abel Deuring
property 'drivers' added to IHWDevice
814
    @property
815
    def drivers(self):
816
        """See `IHWDevice.`"""
817
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
818
        result_set = store.find(HWDriver,
819
                                HWDeviceDriverLink.driver == HWDriver.id,
820
                                HWDeviceDriverLink.device == self)
7604.2.2 by Abel Deuring
implemented reviewr's comments
821
        result_set.order_by((HWDriver.package_name, HWDriver.name))
7604.2.1 by Abel Deuring
property 'drivers' added to IHWDevice
822
        return result_set
823
9094.2.1 by Abel Deuring
allow arbitrary int values for HWDeviceClass.main_class and HWDeviceClass.sub_class; expose HWDeviceClass to the webservice.
824
    @property
825
    def classes(self):
826
        """See `IHWDevice.`"""
827
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
828
        result_set = store.find(
9094.2.2 by Abel Deuring
implemented reviewer's comments
829
            HWDeviceClass,
830
            HWDeviceClass.device == self.id)
9094.2.1 by Abel Deuring
allow arbitrary int values for HWDeviceClass.main_class and HWDeviceClass.sub_class; expose HWDeviceClass to the webservice.
831
        result_set.order_by(HWDeviceClass.main_class, HWDeviceClass.sub_class)
832
        return result_set
833
9094.2.2 by Abel Deuring
implemented reviewer's comments
834
    def getOrCreateDeviceClass(self, main_class, sub_class=None):
9094.2.1 by Abel Deuring
allow arbitrary int values for HWDeviceClass.main_class and HWDeviceClass.sub_class; expose HWDeviceClass to the webservice.
835
        """See `IHWDevice.`"""
836
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
837
        result_set = store.find(
9094.2.2 by Abel Deuring
implemented reviewer's comments
838
            HWDeviceClass,
839
            HWDeviceClass.device == self.id,
9094.2.1 by Abel Deuring
allow arbitrary int values for HWDeviceClass.main_class and HWDeviceClass.sub_class; expose HWDeviceClass to the webservice.
840
            HWDeviceClass.main_class == main_class,
841
            HWDeviceClass.sub_class == sub_class)
842
        existing_record = result_set.one()
843
        if existing_record is not None:
844
            return existing_record
845
        return HWDeviceClass(
846
            device=self, main_class=main_class, sub_class=sub_class)
847
848
    def removeDeviceClass(self, main_class, sub_class=None):
849
        """See `IHWDevice.`"""
850
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
851
        result_set = store.find(
9094.2.2 by Abel Deuring
implemented reviewer's comments
852
            HWDeviceClass,
853
            HWDeviceClass.device == self.id,
9094.2.1 by Abel Deuring
allow arbitrary int values for HWDeviceClass.main_class and HWDeviceClass.sub_class; expose HWDeviceClass to the webservice.
854
            HWDeviceClass.main_class == main_class,
855
            HWDeviceClass.sub_class == sub_class)
856
        existing_record = result_set.one()
857
        if existing_record is not None:
858
            store.remove(existing_record)
859
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
860
861
class HWDeviceSet:
862
    """See `IHWDeviceSet`."""
863
864
    implements(IHWDeviceSet)
865
866
    def create(self, bus, vendor_id, product_id, product_name, variant=None):
867
        """See `IHWDeviceSet`."""
868
        vendor_id_record = HWVendorID.selectOneBy(bus=bus,
869
                                                  vendor_id_for_bus=vendor_id)
870
        if vendor_id_record is None:
6130.11.1 by Abel Deuring
implemented reviewer's comments
871
            # The vendor ID may be unknown for two reasons:
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
872
            #   - we do not have anything like a subscription to newly
873
            #     assigned PCI or USB vendor IDs, so we may get submissions
874
            #     with IDs we don't know about yet.
875
            #   - we may get submissions with invalid IDs.
876
            # In both cases, we create a new HWVendorID entry with the
877
            # vendor name 'Unknown'.
878
            unknown_vendor = HWVendorName.selectOneBy(name=UNKNOWN)
879
            if unknown_vendor is None:
880
                unknown_vendor = HWVendorName(name=UNKNOWN)
881
            vendor_id_record = HWVendorID(bus=bus,
882
                                          vendor_id_for_bus=vendor_id,
883
                                          vendor_name=unknown_vendor)
884
        return HWDevice(bus_vendor=vendor_id_record,
885
                        bus_product_id=product_id, name=product_name,
886
                        variant=variant, submissions=0)
887
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
888
    def getByDeviceID(self, bus, vendor_id, product_id, variant=None):
889
        """See `IHWDeviceSet`."""
890
        if not isValidProductID(bus, product_id):
8063.1.1 by Abel Deuring
better error handling for webservice API queries for HWDB devices with invalid vendor or product IDs.
891
            raise ParameterError(
892
                '%s is not a valid product ID for %s'
893
                % (repr(product_id), bus.title))
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
894
        bus_vendor = HWVendorIDSet().getByBusAndVendorID(bus, vendor_id)
895
        return HWDevice.selectOneBy(bus_vendor=bus_vendor,
896
                                    bus_product_id=product_id,
897
                                    variant=variant)
898
899
    def getOrCreate(self, bus, vendor_id, product_id, product_name,
900
                    variant=None):
901
        """See `IHWDeviceSet`."""
902
        device = self.getByDeviceID(bus, vendor_id, product_id, variant)
903
        if device is None:
904
            return self.create(bus, vendor_id, product_id, product_name,
905
                               variant)
906
        return device
907
7353.2.1 by Abel Deuring
Webservice API for IHWDevice
908
    def getByID(self, id):
909
        """See `IHWDeviceSet`."""
910
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
911
        return store.find(HWDevice, HWDevice.id == id).one()
912
913
    def search(self, bus, vendor_id, product_id=None):
914
        """See `IHWDeviceSet`."""
915
        bus_vendor = HWVendorIDSet().getByBusAndVendorID(bus, vendor_id)
916
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
917
        args = []
918
        if product_id is not None:
8063.1.1 by Abel Deuring
better error handling for webservice API queries for HWDB devices with invalid vendor or product IDs.
919
            if not isValidProductID(bus, product_id):
920
                raise ParameterError(
921
                    '%s is not a valid product ID for %s'
922
                    % (repr(product_id), bus.title))
7353.2.1 by Abel Deuring
Webservice API for IHWDevice
923
            args.append(HWDevice.bus_product_id == product_id)
924
        result_set = store.find(
925
            HWDevice, HWDevice.bus_vendor == bus_vendor, *args)
926
        result_set.order_by(HWDevice.id)
927
        return result_set
928
6130.10.1 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 2
929
930
class HWDeviceNameVariant(SQLBase):
931
    """See `IHWDeviceNameVariant`."""
932
933
    implements(IHWDeviceNameVariant)
934
    _table = 'HWDeviceNameVariant'
935
936
    vendor_name = ForeignKey(dbName='vendor_name', foreignKey='HWVendorName',
937
                             notNull=True)
938
    product_name = StringCol(notNull=True)
939
    device = ForeignKey(dbName='device', foreignKey='HWDevice', notNull=True)
940
    submissions = IntCol(notNull=True)
941
942
943
class HWDeviceNameVariantSet:
944
    """See `IHWDeviceNameVariantSet`."""
945
946
    implements(IHWDeviceNameVariantSet)
947
948
    def create(self, device, vendor_name, product_name):
949
        """See `IHWDeviceNameVariantSet`."""
950
        vendor_name_record = HWVendorName.selectOneBy(name=vendor_name)
951
        if vendor_name_record is None:
952
            vendor_name_record = HWVendorName(name=vendor_name)
953
        return HWDeviceNameVariant(device=device,
954
                                   vendor_name=vendor_name_record,
955
                                   product_name=product_name,
956
                                   submissions=0)
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
957
958
959
class HWDriver(SQLBase):
960
    """See `IHWDriver`."""
961
962
    implements(IHWDriver)
963
    _table = 'HWDriver'
964
7353.2.5 by Abel Deuring
fixed a mal-formed XXX comment
965
    # XXX: Abel Deuring 2008-12-10 bug=306265: package_name should
966
    # be declared notNull=True. This fixes the ambiguity that
967
    # "package_name is None" as well as "package_name == ''" can
968
    # indicate "we don't know to which package this driver belongs",
969
    # moreover, it gives a more clear meaning to the parameter value
970
    #package_name='' in webservice API calls.
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
971
    package_name = StringCol(notNull=False)
972
    name = StringCol(notNull=True)
973
    license = EnumCol(enum=License, notNull=False)
974
7771.2.1 by Tom Berger
allow searching hwdb submissions by driver
975
    def getSubmissions(self, distribution=None, distroseries=None,
976
                       architecture=None, owner=None):
977
        """See `IHWDriver.`"""
978
        return HWSubmissionSet().search(
979
            driver=self, distribution=distribution,
980
            distroseries=distroseries, architecture=architecture, owner=owner)
981
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
982
983
class HWDriverSet:
984
    """See `IHWDriver`."""
985
986
    implements(IHWDriverSet)
987
988
    def create(self, package_name, name, license):
989
        """See `IHWDriverSet`."""
8298.4.1 by Abel Deuring
fix for bug 369769: Create no longer HWDriver records where package_name is None
990
        if package_name is None:
991
            package_name = ''
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
992
        return HWDriver(package_name=package_name, name=name, license=license)
993
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
994
    def getByPackageAndName(self, package_name, name):
995
        """See `IHWDriverSet`."""
8298.4.1 by Abel Deuring
fix for bug 369769: Create no longer HWDriver records where package_name is None
996
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
997
        if package_name in (None, ''):
998
            return store.find(
999
                HWDriver,
8298.4.2 by Abel Deuring
fixed a lint issue
1000
                Or(HWDriver.package_name == None,
1001
                   HWDriver.package_name == ''),
8298.4.1 by Abel Deuring
fix for bug 369769: Create no longer HWDriver records where package_name is None
1002
                HWDriver.name == name).one()
1003
        else:
1004
            return store.find(
1005
                HWDriver, HWDriver.package_name == package_name,
1006
                HWDriver.name == name).one()
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
1007
1008
    def getOrCreate(self, package_name, name, license=None):
1009
        """See `IHWDriverSet`."""
8298.4.1 by Abel Deuring
fix for bug 369769: Create no longer HWDriver records where package_name is None
1010
        # Bugs 306265, 369769: If the method parameter package_name is
1011
        # None, and if no matching record exists, we create new records
1012
        # with package_name = '', but we must also search for old records
1013
        # where package_name == None in order to avoid the creation of
1014
        # two records where on rcord has package_name=None and the other
1015
        # package_name=''.
1016
        driver = self.getByPackageAndName(package_name, name)
1017
1018
        if driver is None:
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
1019
            return self.create(package_name, name, license)
8298.4.1 by Abel Deuring
fix for bug 369769: Create no longer HWDriver records where package_name is None
1020
        return driver
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
1021
7353.2.3 by Abel Deuring
Webservice API for table HWDriver
1022
    def search(self, package_name=None, name=None):
1023
        """See `IHWDriverSet`."""
1024
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1025
        args = []
1026
        if package_name is not None:
1027
            if len(package_name) == 0:
1028
                args.append(Or(HWDriver.package_name == None,
1029
                               HWDriver.package_name == ''))
1030
            else:
1031
                args.append(HWDriver.package_name == package_name)
1032
        if name != None:
1033
            args.append(HWDriver.name == name)
1034
        result_set = store.find(HWDriver, *args)
1035
        return result_set.order_by(HWDriver.id)
1036
7353.2.6 by Abel Deuring
added missing implementation to access a single HWDriver instance via the webservice API
1037
    def getByID(self, id):
1038
        """See `IHWDriverSet`."""
1039
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1040
        return store.find(HWDriver, HWDriver.id == id).one()
1041
8538.3.2 by Abel Deuring
implemented reviewer's comments
1042
    def all_driver_names(self):
1043
        """See `IHWDriverSet`."""
1044
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1045
        result = store.find(HWDriverName)
1046
        result.order_by(HWDriverName.name)
1047
        return result
1048
1049
    def all_package_names(self):
1050
        """See `IHWDriverSet`."""
1051
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
8687.7.2 by Abel Deuring
XXX added as a reminder to remove the new clause preventing package_name==None in HWDriverPackageName once bug 306265 is fixed.
1052
        # XXX Abel Deuring 2009-06-19 The clause package_name != None
1053
        # can be removed once bug #306265 is fixed.
8687.7.1 by Abel Deuring
fixed an exception when the set distinct driver packages names is retrieved via the webservice API if the value package_name=None exists.
1054
        result = store.find(HWDriverPackageName,
1055
                            HWDriverPackageName.package_name != None)
8538.3.2 by Abel Deuring
implemented reviewer's comments
1056
        result.order_by(HWDriverPackageName.package_name)
1057
        return result
7745.1.1 by Abel Deuring
property HWDriverSet.package_names added and exposed to the webservice API
1058
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1059
8538.3.1 by Abel Deuring
Python classes for SQL views hwdrivername and hwdriverpackagename added
1060
class HWDriverName(SQLBase):
1061
    """See `IHWDriverName`."""
1062
1063
    implements(IHWDriverName)
1064
    _table = 'HWDriverNames'
1065
1066
    name = StringCol(notNull=True)
1067
1068
1069
class HWDriverPackageName(SQLBase):
1070
    """See `IHWDriverPackageName`."""
1071
1072
    implements(IHWDriverPackageName)
1073
    _table = 'HWDriverPackageNames'
1074
1075
    package_name = StringCol(notNull=True)
1076
1077
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1078
class HWDeviceDriverLink(SQLBase):
6407.1.5 by Abel Deuring
implemented reviewer's comments
1079
    """See `IHWDeviceDriverLink`."""
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1080
1081
    implements(IHWDeviceDriverLink)
1082
    _table = 'HWDeviceDriverLink'
1083
1084
    device = ForeignKey(dbName='device', foreignKey='HWDevice', notNull=True)
1085
    driver = ForeignKey(dbName='driver', foreignKey='HWDriver', notNull=False)
1086
1087
1088
class HWDeviceDriverLinkSet:
6407.1.5 by Abel Deuring
implemented reviewer's comments
1089
    """See `IHWDeviceDriverLinkSet`."""
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1090
1091
    implements(IHWDeviceDriverLinkSet)
1092
1093
    def create(self, device, driver):
1094
        """See `IHWDeviceDriverLinkSet`."""
1095
        return HWDeviceDriverLink(device=device, driver=driver)
1096
6407.1.1 by Abel Deuring
methods to retrieve data from HWDB device tables; HWBus.PPCARD added
1097
    def getByDeviceAndDriver(self, device, driver):
1098
        """See `IHWDeviceDriverLink`."""
1099
        return HWDeviceDriverLink.selectOneBy(device=device, driver=driver)
1100
1101
    def getOrCreate(self, device, driver):
1102
        """See `IHWDeviceDriverLink`."""
1103
        device_driver_link = self.getByDeviceAndDriver(device, driver)
1104
        if device_driver_link is None:
1105
            return self.create(device, driver)
1106
        return device_driver_link
1107
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1108
6740.4.1 by Abel Deuring
core implementation of class HWDeviceClass
1109
class HWDeviceClass(SQLBase):
1110
    """See `IHWDeviceClass`."""
1111
    implements(IHWDeviceClass)
1112
1113
    device = ForeignKey(dbName='device', foreignKey='HWDevice', notNull=True)
9094.2.1 by Abel Deuring
allow arbitrary int values for HWDeviceClass.main_class and HWDeviceClass.sub_class; expose HWDeviceClass to the webservice.
1114
    main_class = IntCol(notNull=True)
1115
    sub_class = IntCol(notNull=False)
6740.4.1 by Abel Deuring
core implementation of class HWDeviceClass
1116
9094.2.2 by Abel Deuring
implemented reviewer's comments
1117
    def delete(self):
1118
        """See `IHWDeviceClass`."""
1119
        store = Store.of(self)
1120
        store.remove(self)
1121
1122
1123
class HWDeviceClassSet:
1124
    """See `IHWDeviceClassSet`."""
1125
    implements(IHWDeviceClassSet)
1126
1127
    def get(self, id):
1128
        """See `IHWDeviceClassSet`."""
1129
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1130
        return store.find(HWDeviceClass, HWDeviceClass.id == id).one()
1131
6740.4.1 by Abel Deuring
core implementation of class HWDeviceClass
1132
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1133
class HWSubmissionDevice(SQLBase):
1134
    """See `IHWSubmissionDevice`."""
1135
1136
    implements(IHWSubmissionDevice)
1137
    _table = 'HWSubmissionDevice'
1138
1139
    device_driver_link = ForeignKey(dbName='device_driver_link',
1140
                                    foreignKey='HWDeviceDriverLink',
1141
                                    notNull=True)
1142
    submission = ForeignKey(dbName='submission', foreignKey='HWSubmission',
1143
                            notNull=True)
1144
    parent = ForeignKey(dbName='parent', foreignKey='HWSubmissionDevice',
1145
                        notNull=False)
1146
6456.1.1 by Abel Deuring
column hal_device_id added to HWSubmissionDevice
1147
    hal_device_id = IntCol(notNull=True)
1148
7525.3.1 by Abel Deuring
Webservice API for IHWSubmissionDevice
1149
    @property
1150
    def device(self):
1151
        """See `IHWSubmissionDevice`."""
1152
        return self.device_driver_link.device
1153
1154
    @property
1155
    def driver(self):
1156
        """See `IHWSubmissionDevice`."""
1157
        return self.device_driver_link.driver
1158
6456.1.1 by Abel Deuring
column hal_device_id added to HWSubmissionDevice
1159
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1160
class HWSubmissionDeviceSet:
1161
    """See `IHWSubmissionDeviceSet`."""
1162
1163
    implements(IHWSubmissionDeviceSet)
1164
6456.1.1 by Abel Deuring
column hal_device_id added to HWSubmissionDevice
1165
    def create(self, device_driver_link, submission, parent, hal_device_id):
6130.10.2 by Abel Deuring
SQLBase derived classes for HWDB device tables, part 3
1166
        """See `IHWSubmissionDeviceSet`."""
1167
        return HWSubmissionDevice(device_driver_link=device_driver_link,
6456.1.1 by Abel Deuring
column hal_device_id added to HWSubmissionDevice
1168
                                  submission=submission, parent=parent,
1169
                                  hal_device_id=hal_device_id)
6456.2.7 by Abel Deuring
Method added to HWSubmissionDeviceSet to retrieve devices of a submission
1170
6456.2.9 by Abel Deuring
implemented reviewer's comments
1171
    def getDevices(self, submission):
6456.2.7 by Abel Deuring
Method added to HWSubmissionDeviceSet to retrieve devices of a submission
1172
        """See `IHWSubmissionDeviceSet`."""
1173
        return HWSubmissionDevice.selectBy(
1174
            submission=submission,
1175
            orderBy=['parent', 'device_driver_link', 'hal_device_id'])
6456.2.10 by Abel Deuring
RF merge
1176
7525.3.2 by Abel Deuring
implemented reviewer's comments
1177
    def get(self, id):
7525.3.1 by Abel Deuring
Webservice API for IHWSubmissionDevice
1178
        """See `IHWSubmissionDeviceSet`."""
1179
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1180
        return store.find(
1181
            HWSubmissionDevice, HWSubmissionDevice.id == id).one()
1182
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
1183
    def numDevicesInSubmissions(
8697.19.1 by Abel Deuring
allow driver-only queries for HWDB statistics methods.
1184
        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
1185
        package_name=None, distro_target=None):
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
1186
        """See `IHWSubmissionDeviceSet`."""
1187
        store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1188
1189
        tables, where_clauses = make_submission_device_statistics_clause(
8697.19.1 by Abel Deuring
allow driver-only queries for HWDB statistics methods.
1190
            bus, vendor_id, product_id, driver_name, package_name, False)
8311.2.2 by Abel Deuring
changed the function num_devices_in_submissions and num_submissions_with_device into mehtods of HWSubmissionDeviceSet resp. HWSubmissionSet
1191
1192
        distro_tables, distro_clauses = make_distro_target_clause(
1193
            distro_target)
1194
        if distro_clauses:
1195
            tables.extend(distro_tables)
1196
            where_clauses.extend(distro_clauses)
1197
            where_clauses.append(
1198
                HWSubmissionDevice.submission == HWSubmission.id)
1199
1200
        result = store.execute(
1201
            Select(
1202
                columns=[Count()], tables=tables, where=And(*where_clauses)))
1203
        return result.get_one()[0]
1204
6796.1.1 by Abel Deuring
Definition of HWSubmissionBug, linking HWDB submissions with bugs
1205
1206
class HWSubmissionBug(SQLBase):
1207
    """See `IHWSubmissionBug`."""
1208
1209
    implements(IHWSubmissionBug)
1210
    _table = 'HWSubmissionBug'
1211
1212
    submission = ForeignKey(dbName='submission', foreignKey='HWSubmission',
1213
                              notNull=True)
1214
    bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True)
1215
8486.18.1 by Abel Deuring
Methods added to class Bug to add and remove links between a bug and a HWDB submission.
1216
6796.1.1 by Abel Deuring
Definition of HWSubmissionBug, linking HWDB submissions with bugs
1217
class HWSubmissionBugSet:
1218
    """See `IHWSubmissionBugSet`."""
1219
1220
    implements(IHWSubmissionBugSet)
1221
1222
    def create(self, submission, bug):
1223
        """See `IHWSubmissionBugSet`."""
8486.18.8 by Abel Deuring
implemented reviewer's comments
1224
        store = Store.of(bug)
8486.18.5 by Abel Deuring
exposed bug.linkHWSubmission()m bug.unlinkHWSubmission and bug.getHWSubmissions() to the webservice API.
1225
        existing_link = store.find(
1226
            HWSubmissionBug,
1227
            And(HWSubmissionBug.submission == submission,
1228
                HWSubmissionBug.bug == bug)).one()
1229
        if existing_link is not None:
1230
            return existing_link
6796.1.1 by Abel Deuring
Definition of HWSubmissionBug, linking HWDB submissions with bugs
1231
        return HWSubmissionBug(submission=submission, bug=bug)
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1232
8486.18.1 by Abel Deuring
Methods added to class Bug to add and remove links between a bug and a HWDB submission.
1233
    def remove(self, submission, bug):
1234
        """See `IHWSubmissionBugSet`."""
8486.18.8 by Abel Deuring
implemented reviewer's comments
1235
        store = Store.of(bug)
8486.18.1 by Abel Deuring
Methods added to class Bug to add and remove links between a bug and a HWDB submission.
1236
        link = store.find(
1237
            HWSubmissionBug,
1238
            And(HWSubmissionBug.bug == bug,
1239
                HWSubmissionBug.submission == submission.id)).one()
1240
        if link is not None:
1241
            store.remove(link)
1242
1243
    def submissionsForBug(self, bug, user=None):
1244
        """See `IHWSubmissionBugSet`."""
8486.18.8 by Abel Deuring
implemented reviewer's comments
1245
        store = Store.of(bug)
8486.18.1 by Abel Deuring
Methods added to class Bug to add and remove links between a bug and a HWDB submission.
1246
        result = store.find(
1247
            HWSubmission, And(HWSubmissionBug.bug == bug,
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
1248
                              HWSubmissionBug.submission == HWSubmission.id,
1249
                              _userCanAccessSubmissionStormClause(user)))
8486.18.1 by Abel Deuring
Methods added to class Bug to add and remove links between a bug and a HWDB submission.
1250
        result.order_by(HWSubmission.submission_key)
1251
        return result
1252
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1253
1254
def make_submission_device_statistics_clause(
8451.2.2 by Abel Deuring
allow to call deviceDriverOwnersAffectedByBugs only with a driver
1255
    bus, vendor_id, product_id, driver_name, package_name,
1256
    device_ids_required):
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1257
    """Create a where expression and a table list for selecting devices.
1258
    """
8630.1.1 by Abel Deuring
Fix performance issues of deviceDriverOwnersAffectedByBugs when called with a given driver but without a device.
1259
    tables = [HWSubmissionDevice, HWDeviceDriverLink]
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1260
    where_clauses = [
1261
        HWSubmissionDevice.device_driver_link == HWDeviceDriverLink.id,
1262
        ]
1263
8451.2.2 by Abel Deuring
allow to call deviceDriverOwnersAffectedByBugs only with a driver
1264
    if device_ids_required:
1265
        if bus is None or vendor_id is None or product_id is None:
1266
            raise ParameterError("Device IDs are required.")
1267
    else:
1268
        device_specified = [
1269
            param
1270
            for param in (bus, vendor_id, product_id)
1271
            if param is not None]
1272
1273
        if len(device_specified) not in (0, 3):
1274
            raise ParameterError(
1275
                'Either specify bus, vendor_id and product_id or none of '
1276
                'them.')
1277
        if bus is None and driver_name is None:
1278
            raise ParameterError(
1279
                'Specify (bus, vendor_id, product_id) or driver_name.')
1280
    if bus is not None:
8630.1.1 by Abel Deuring
Fix performance issues of deviceDriverOwnersAffectedByBugs when called with a given driver but without a device.
1281
        tables.extend([HWVendorID, HWDevice])
8451.2.2 by Abel Deuring
allow to call deviceDriverOwnersAffectedByBugs only with a driver
1282
        where_clauses.extend([
1283
            HWVendorID.bus == bus,
1284
            HWVendorID.vendor_id_for_bus == vendor_id,
1285
            HWDevice.bus_vendor == HWVendorID.id,
1286
            HWDeviceDriverLink.device == HWDevice.id,
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
1287
            HWDevice.bus_product_id == product_id])
8451.2.2 by Abel Deuring
allow to call deviceDriverOwnersAffectedByBugs only with a driver
1288
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1289
    if driver_name is None and package_name is None:
1290
        where_clauses.append(HWDeviceDriverLink.driver == None)
1291
    else:
1292
        tables.append(HWDriver)
1293
        where_clauses.append(HWDeviceDriverLink.driver == HWDriver.id)
1294
        if driver_name is not None:
1295
            where_clauses.append(HWDriver.name == driver_name)
1296
        if package_name is not None:
8311.2.3 by Abel Deuring
implemented revier's comments
1297
            if package_name == '':
8311.2.4 by Abel Deuring
fixed a stupid mistake in a comment
1298
                # XXX Abel Deuring, 2009-05-07, bug=306265. package_name
1299
                # should be declared notNull=True. For now, we must query
1300
                # for the empty string as well as for None.
8311.2.3 by Abel Deuring
implemented revier's comments
1301
                where_clauses.append(
1302
                    Or(HWDriver.package_name == package_name,
1303
                       HWDriver.package_name == None))
1304
            else:
1305
                where_clauses.append(HWDriver.package_name == package_name)
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1306
1307
    return tables, where_clauses
1308
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
1309
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1310
def make_distro_target_clause(distro_target):
1311
    """Create a where expression and a table list to limit results to a
1312
    distro target.
1313
    """
1314
    if distro_target is not None:
1315
        if IDistroArchSeries.providedBy(distro_target):
1316
            return (
1317
                [HWSubmission],
8311.2.3 by Abel Deuring
implemented revier's comments
1318
                [HWSubmission.distroarchseries == distro_target.id])
8311.2.1 by Abel Deuring
Functions to count how often a given device appears in HWDB subissions and how many HWDB submissions contain a given device.
1319
        elif IDistroSeries.providedBy(distro_target):
1320
            return (
1321
                [DistroArchSeries, HWSubmission],
1322
                [
1323
                    HWSubmission.distroarchseries == DistroArchSeries.id,
1324
                    DistroArchSeries.distroseries == distro_target.id,
1325
                    ])
1326
        elif IDistribution.providedBy(distro_target):
1327
            return (
1328
                [DistroArchSeries, DistroSeries, HWSubmission],
1329
                [
1330
                    HWSubmission.distroarchseries == DistroArchSeries.id,
1331
                    DistroArchSeries.distroseries == DistroSeries.id,
1332
                    DistroSeries.distribution == distro_target.id,
1333
                    ])
1334
        else:
1335
            raise ValueError(
1336
                'Parameter distro_target must be an IDistribution, '
1337
                'IDistroSeries or IDistroArchSeries')
1338
    return ([], [])
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
1339
7675.166.301 by Stuart Bishop
Replace In(col, i) with col.is_in(u) to work around Bug #670906 and delint
1340
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
1341
def _userCanAccessSubmissionStormClause(user):
1342
    """Limit results of HWSubmission queries to rows the user can access.
1343
    """
1344
    submission_is_public = Not(HWSubmission.private)
1345
    admins = getUtility(ILaunchpadCelebrities).admin
1346
    janitor = getUtility(ILaunchpadCelebrities).janitor
1347
    if user is None:
1348
        return submission_is_public
1349
    elif user.inTeam(admins) or user == janitor:
1350
        return True
1351
    else:
1352
        public = Not(HWSubmission.private)
1353
        subselect = Select(
1354
            TeamParticipation.teamID,
1355
            And(HWSubmission.ownerID == TeamParticipation.teamID,
8630.1.1 by Abel Deuring
Fix performance issues of deviceDriverOwnersAffectedByBugs when called with a given driver but without a device.
1356
                TeamParticipation.personID == user.id,
1357
                HWSubmission.private))
8486.18.2 by Abel Deuring
changed method HWSubmissionSet._userHasAccessStormClause() into a function; privacy check added in HWSubmissionBugSet.submissionsForBug() added; tests added.
1358
        has_access = HWSubmission.ownerID.is_in(subselect)
1359
        return Or(public, has_access)