~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/model/person.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2010-08-17 07:03:25 UTC
  • mfrom: (11307.2.20 registry)
  • Revision ID: launchpad@pqm.canonical.com-20100817070325-paezn3lnovq782re
[r=henninge, sinzui][ui=none][bug=615237] Reduce the query count for
        team/participants to 11 from hundreds for ubuntu-dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
48
48
    StringCol)
49
49
from sqlobject.sqlbuilder import AND, OR, SQLConstant
50
50
from storm.store import EmptyResultSet, Store
51
 
from storm.expr import And, In, Join, LeftJoin, Lower, Not, Or, SQL
 
51
from storm.expr import (
 
52
    Alias, And, Exists, In, Join, LeftJoin, Lower, Min, Not, Or, Select, SQL,
 
53
    )
52
54
from storm.info import ClassAlias
53
55
 
54
56
from canonical.config import config
63
65
 
64
66
from canonical.lazr.utils import get_current_browser_request, safe_hasattr
65
67
 
 
68
from canonical.launchpad.components.decoratedresultset import DecoratedResultSet
66
69
from canonical.launchpad.database.account import Account, AccountPassword
67
70
from canonical.launchpad.interfaces.account import AccountSuspendedError
68
71
from lp.bugs.model.bugtarget import HasBugsBase
1011
1014
        result = result.order_by(KarmaCategory.title)
1012
1015
        return [karma_cache for (karma_cache, category) in result]
1013
1016
 
1014
 
    @property
 
1017
    @cachedproperty('_karma_cached')
1015
1018
    def karma(self):
1016
1019
        """See `IPerson`."""
 
1020
        # May also be loaded from _all_members
1017
1021
        cache = KarmaTotalCache.selectOneBy(person=self)
1018
1022
        if cache is None:
1019
1023
            # Newly created accounts may not be in the cache yet, meaning the
1031
1035
 
1032
1036
        return self.is_valid_person
1033
1037
 
1034
 
    @property
 
1038
    @cachedproperty('_is_valid_person_cached')
1035
1039
    def is_valid_person(self):
1036
1040
        """See `IPerson`."""
1037
1041
        if self.is_team:
1430
1434
    @property
1431
1435
    def allmembers(self):
1432
1436
        """See `IPerson`."""
1433
 
        query = """
1434
 
            Person.id = TeamParticipation.person AND
1435
 
            TeamParticipation.team = %s AND
1436
 
            TeamParticipation.person != %s
1437
 
            """ % sqlvalues(self.id, self.id)
1438
 
        return Person.select(query, clauseTables=['TeamParticipation'])
1439
 
 
1440
 
    def _getMembersWithPreferredEmails(self, include_teams=False):
 
1437
        return self._all_members()
 
1438
 
 
1439
    @property
 
1440
    def all_members_prepopulated(self):
 
1441
        """See `IPerson`."""
 
1442
        return self._all_members(need_karma=True, need_ubuntu_coc=True,
 
1443
            need_location=True, need_archive=True, need_preferred_email=True,
 
1444
            need_validity=True)
 
1445
 
 
1446
    def _all_members(self, need_karma=False, need_ubuntu_coc=False,
 
1447
        need_location=False, need_archive=False, need_preferred_email=False,
 
1448
        need_validity=False):
 
1449
        """Lookup all members of the team with optional precaching.
 
1450
        
 
1451
        :param need_karma: The karma attribute will be cached.
 
1452
        :param need_ubuntu_coc: The is_ubuntu_coc_signer attribute will be
 
1453
            cached.
 
1454
        :param need_location: The location attribute will be cached.
 
1455
        :param need_archive: The archive attribute will be cached.
 
1456
        :param need_preferred_email: The preferred email attribute will be
 
1457
            cached.
 
1458
        :param need_validity: The is_valid attribute will be cached.
 
1459
        """
 
1460
        # TODO: consolidate this with getMembersWithPreferredEmails.
 
1461
        #       The difference between the two is that
 
1462
        #       getMembersWithPreferredEmails includes self, which is arguably
 
1463
        #       wrong, but perhaps deliberate.
 
1464
        store = Store.of(self)
 
1465
        origin = [
 
1466
            Person,
 
1467
            Join(TeamParticipation, TeamParticipation.person == Person.id),
 
1468
            ]
 
1469
        conditions = And(
 
1470
            # Members of this team,
 
1471
            TeamParticipation.team == self.id,
 
1472
            # But not the team itself.
 
1473
            TeamParticipation.person != self.id)
 
1474
        columns = [Person]
 
1475
        if need_karma:
 
1476
            # New people have no karmatotalcache rows.
 
1477
            origin.append(
 
1478
                LeftJoin(KarmaTotalCache, KarmaTotalCache.person == Person.id))
 
1479
            columns.append(KarmaTotalCache)
 
1480
        if need_ubuntu_coc:
 
1481
            columns.append(Alias(Exists(Select(SignedCodeOfConduct,
 
1482
                AND(Person._is_ubuntu_coc_signer_condition(),
 
1483
                    SignedCodeOfConduct.ownerID == Person.id))),
 
1484
                name='is_ubuntu_coc_signer'))
 
1485
        if need_location:
 
1486
            # New people have no location rows
 
1487
            origin.append(
 
1488
                LeftJoin(PersonLocation, PersonLocation.person == Person.id))
 
1489
            columns.append(PersonLocation)
 
1490
        if need_archive:
 
1491
            # Not everyone has PPA's 
 
1492
            # It would be nice to cleanly expose the soyuz rules for this to avoid
 
1493
            # duplicating the relationships.
 
1494
            origin.append(
 
1495
                LeftJoin(Archive, Archive.owner == Person.id))
 
1496
            columns.append(Archive)
 
1497
            conditions = And(conditions,
 
1498
                Or(Archive.id == None, And(
 
1499
                Archive.id == Select(Min(Archive.id),
 
1500
                    Archive.owner == Person.id, Archive),
 
1501
                Archive.purpose == ArchivePurpose.PPA)))
 
1502
        if need_preferred_email:
 
1503
            # Teams don't have email.
 
1504
            origin.append(
 
1505
                LeftJoin(EmailAddress, EmailAddress.person == Person.id))
 
1506
            columns.append(EmailAddress)
 
1507
            conditions = And(conditions,
 
1508
                Or(EmailAddress.status == None,
 
1509
                    EmailAddress.status == EmailAddressStatus.PREFERRED))
 
1510
        if need_validity:
 
1511
            # May find invalid persons
 
1512
            origin.append(
 
1513
                LeftJoin(ValidPersonCache, ValidPersonCache.id == Person.id))
 
1514
            columns.append(ValidPersonCache)
 
1515
        if len(columns) == 1:
 
1516
            columns = columns[0]
 
1517
            # Return a simple ResultSet
 
1518
            return store.using(*origin).find(columns, conditions)
 
1519
        # Adapt the result into a cached Person.
 
1520
        columns = tuple(columns)
 
1521
        raw_result = store.using(*origin).find(columns, conditions)
 
1522
        def prepopulate_person(row):
 
1523
            result = row[0]
 
1524
            index = 1
 
1525
            #-- karma caching
 
1526
            if need_karma:
 
1527
                karma = row[index]
 
1528
                index += 1
 
1529
                if karma is None:
 
1530
                    karma_total = 0
 
1531
                else:
 
1532
                    karma_total = karma.karma_total
 
1533
                cache_property(result, '_karma_cached', karma_total)
 
1534
            #-- ubuntu code of conduct signer status caching.
 
1535
            if need_ubuntu_coc:
 
1536
                signed = row[index]
 
1537
                index += 1
 
1538
                cache_property(result, '_is_ubuntu_coc_signer_cached', signed)
 
1539
            #-- location caching
 
1540
            if need_location:
 
1541
                location = row[index]
 
1542
                index += 1
 
1543
                cache_property(result, '_location', location)
 
1544
            #-- archive caching
 
1545
            if need_archive:
 
1546
                archive = row[index]
 
1547
                index += 1
 
1548
                cache_property(result, '_archive_cached', archive)
 
1549
            #-- preferred email caching
 
1550
            if need_preferred_email:
 
1551
                email = row[index]
 
1552
                index += 1
 
1553
                cache_property(result, '_preferredemail_cached', email)
 
1554
            if need_validity:
 
1555
                valid = row[index] is not None
 
1556
                index += 1
 
1557
                cache_property(result, '_is_valid_person_cached', valid)
 
1558
            return result
 
1559
        return DecoratedResultSet(raw_result, result_decorator=prepopulate_person)
 
1560
 
 
1561
    def _getMembersWithPreferredEmails(self):
1441
1562
        """Helper method for public getMembersWithPreferredEmails.
1442
1563
 
1443
1564
        We can't return the preferred email address directly to the
1455
1576
            EmailAddress.status == EmailAddressStatus.PREFERRED)
1456
1577
        return store.using(*origin).find((Person, EmailAddress), conditions)
1457
1578
 
1458
 
    def getMembersWithPreferredEmails(self, include_teams=False):
 
1579
    def getMembersWithPreferredEmails(self):
1459
1580
        """See `IPerson`."""
1460
 
        result = self._getMembersWithPreferredEmails(
1461
 
            include_teams=include_teams)
 
1581
        result = self._getMembersWithPreferredEmails()
1462
1582
        person_list = []
1463
1583
        for person, email in result:
1464
1584
            cache_property(person, '_preferredemail_cached', email)
1465
1585
            person_list.append(person)
1466
1586
        return person_list
1467
1587
 
1468
 
    def getMembersWithPreferredEmailsCount(self, include_teams=False):
 
1588
    def getMembersWithPreferredEmailsCount(self):
1469
1589
        """See `IPerson`."""
1470
 
        result = self._getMembersWithPreferredEmails(
1471
 
            include_teams=include_teams)
 
1590
        result = self._getMembersWithPreferredEmails()
1472
1591
        return result.count()
1473
1592
 
1474
1593
    @property
2273
2392
            distribution.main_archive, self)
2274
2393
        return permissions.count() > 0
2275
2394
 
2276
 
    @cachedproperty
 
2395
    @cachedproperty('_is_ubuntu_coc_signer_cached')
2277
2396
    def is_ubuntu_coc_signer(self):
2278
2397
        """See `IPerson`."""
 
2398
        # Also assigned to by self._all_members.
 
2399
        store = Store.of(self)
 
2400
        query = AND(SignedCodeOfConduct.ownerID == self.id,
 
2401
            Person._is_ubuntu_coc_signer_condition())
 
2402
        # TODO: Using exists would be faster than count().
 
2403
        return bool(store.find(SignedCodeOfConduct, query).count())
 
2404
 
 
2405
    @staticmethod
 
2406
    def _is_ubuntu_coc_signer_condition():
 
2407
        """Generate a Storm Expr for determing the coc signing status."""
2279
2408
        sigset = getUtility(ISignedCodeOfConductSet)
2280
2409
        lastdate = sigset.getLastAcceptedDate()
2281
 
 
2282
 
        query = AND(SignedCodeOfConduct.q.active==True,
2283
 
                    SignedCodeOfConduct.q.ownerID==self.id,
2284
 
                    SignedCodeOfConduct.q.datecreated>=lastdate)
2285
 
 
2286
 
        return bool(SignedCodeOfConduct.select(query).count())
 
2410
        return AND(SignedCodeOfConduct.active == True,
 
2411
            SignedCodeOfConduct.datecreated >= lastdate)
2287
2412
 
2288
2413
    @property
2289
2414
    def activesignatures(self):
2297
2422
        sCoC_util = getUtility(ISignedCodeOfConductSet)
2298
2423
        return sCoC_util.searchByUser(self.id, active=False)
2299
2424
 
2300
 
    @property
 
2425
    @cachedproperty('_archive_cached')
2301
2426
    def archive(self):
2302
2427
        """See `IPerson`."""
2303
2428
        return getUtility(IArchiveSet).getPPAOwnedByPerson(self)