~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/doc/personlocation.txt

[r=sinzui][bug=855670] Add additional checks to the private team
        launchpad.LimitedView security adaptor so more users in defined
        roles can see the team.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Locations for People and Teams
 
2
==============================
 
3
 
 
4
The PersonLocation object stores information about the location and time
 
5
zone of a person.  It also remembers who provided that information, and
 
6
when. This is designed to make it possible to have people provide
 
7
location / time zone info for other people in a wiki style.
 
8
 
 
9
    >>> from canonical.launchpad.webapp.testing import verifyObject
 
10
    >>> from lp.registry.interfaces.location import IObjectWithLocation
 
11
    >>> from lp.registry.interfaces.person import IPersonSet
 
12
    >>> personset = getUtility(IPersonSet)
 
13
 
 
14
A Person implements the IObjectWithLocation interface.
 
15
 
 
16
    >>> login('test@canonical.com')
 
17
    >>> marilize = personset.getByName('marilize')
 
18
    >>> verifyObject(IObjectWithLocation, marilize)
 
19
    True
 
20
 
 
21
A Person has a PersonLocation record, if there is any location
 
22
information associated with them. That implements the IPersonLocation
 
23
interface.
 
24
 
 
25
    >>> from lp.registry.interfaces.location import IPersonLocation
 
26
    >>> marilize.location
 
27
    <PersonLocation...
 
28
    >>> verifyObject(IPersonLocation, marilize.location)
 
29
    True
 
30
 
 
31
In some cases, a person has a time zone, but no location.
 
32
 
 
33
    >>> print marilize.time_zone
 
34
    Africa/Maseru
 
35
    >>> print marilize.latitude
 
36
    None
 
37
 
 
38
The location for a person is set with the "setLocation" method. This
 
39
requires that the user providing the information is passed as a
 
40
parameter.
 
41
 
 
42
A user cannot set another user's location.
 
43
 
 
44
    >>> jdub = personset.getByName('jdub')
 
45
    >>> login_person(jdub)
 
46
    >>> cprov = personset.getByName('cprov')
 
47
    >>> cprov.setLocation(-43.0, -62.1, 'America/Sao_Paulo', jdub)
 
48
    Traceback (most recent call last):
 
49
    ...
 
50
    Unauthorized:...
 
51
 
 
52
A user can set his own location.
 
53
 
 
54
    >>> login_person(cprov)
 
55
    >>> cprov.setLocation(-43.2, -61.93, 'America/Sao_Paulo', cprov)
 
56
 
 
57
cprov can change his location information. We need to deal
 
58
with some floating point precision issues here, hence the rounding.
 
59
 
 
60
    >>> login_person(cprov)
 
61
    >>> cprov.setLocation(-43.52, -61.93, 'America/Sao_Paulo', cprov)
 
62
    >>> abs(cprov.latitude + 43.52) < 0.001
 
63
    True
 
64
 
 
65
Admins can set a user's location.
 
66
 
 
67
    >>> admin = personset.getByName('name16')
 
68
    >>> login_person(admin)
 
69
    >>> cprov.setLocation(-43.0, -62.1, 'America/Sao_Paulo', admin)
 
70
    >>> abs(cprov.longitude + 62.1) < 0.001
 
71
    True
 
72
 
 
73
We cannot store a location for a team, though.
 
74
 
 
75
    >>> guadamen = personset.getByName('guadamen')
 
76
    >>> guadamen.setLocation(34.5, 23.1, 'Africa/Maseru', jdub)
 
77
    Traceback (most recent call last):
 
78
    ...
 
79
    AssertionError:...
 
80
 
 
81
Nor can we set only the latitude of a person.
 
82
 
 
83
    >>> cprov.setLocation(-43.0, None, 'America/Sao_Paulo', admin)
 
84
    Traceback (most recent call last):
 
85
    ...
 
86
    AssertionError:...
 
87
 
 
88
Similarly, we can't set only the longitude.
 
89
 
 
90
    >>> cprov.setLocation(None, -43.0, 'America/Sao_Paulo', admin)
 
91
    Traceback (most recent call last):
 
92
    ...
 
93
    AssertionError:...
 
94
 
 
95
We can get lists of the participants in a team that do, or do not, have
 
96
locations. Specifically, we mean latitude/longitude data, not time zone
 
97
data.
 
98
 
 
99
When we get mapped participants, and unmapped participants, we only mean
 
100
the individuals, not other teams. We'll show that guadamen has a
 
101
sub-team, ubuntu-team, and that it still does not appear in either
 
102
mapped_participants or unmapped_participants (although its members do).
 
103
 
 
104
    >>> for member in guadamen.activemembers:
 
105
    ...     if member.teamowner is not None:
 
106
    ...         print member.name
 
107
    ubuntu-team
 
108
    >>> len(guadamen.getMappedParticipants())
 
109
    2
 
110
    >>> for mapped in guadamen.getMappedParticipants():
 
111
    ...     if mapped.teamowner is not None:
 
112
    ...         print mapped.name
 
113
    >>> guadamen.unmapped_participants.count()
 
114
    7
 
115
    >>> for unmapped in guadamen.unmapped_participants:
 
116
    ...     if unmapped.teamowner is not None:
 
117
    ...         print unmapped.name
 
118
 
 
119
When we iterate over the mapped_participants in a team, their locations
 
120
have been pre-cached so that we don't hit the database everytime we
 
121
access a person's .location property.
 
122
 
 
123
    >>> from lp.services.propertycache import get_property_cache
 
124
    >>> for mapped in guadamen.getMappedParticipants():
 
125
    ...     cache = get_property_cache(mapped)
 
126
    ...     if ("location" not in cache or
 
127
    ...         not verifyObject(IPersonLocation, cache.location)):
 
128
    ...         print 'No cached location on %s' % mapped.name
 
129
 
 
130
The mapped_participants methods takes a optional argument to limit the
 
131
number of persons in the returned list.
 
132
 
 
133
    >>> mapped = guadamen.getMappedParticipants(limit=1)
 
134
    >>> len(mapped)
 
135
    1
 
136
 
 
137
The count of mapped and unmapped members can also be retrieved, which is
 
138
faster than getting the resultset of members.
 
139
 
 
140
    >>> guadamen.mapped_participants_count
 
141
    2
 
142
    >>> guadamen.unmapped_participants_count
 
143
    7
 
144
 
 
145
The bounds of the mapped members can be retrieved. It is a dict that contains
 
146
the minimum maximum, and central longitudes and latitudes.
 
147
 
 
148
    >>> bounds = guadamen.getMappedParticipantsBounds()
 
149
    >>> for key in sorted(bounds):
 
150
    ...     print "%s: %s" % (key, bounds[key])
 
151
    center_lat: 4...
 
152
    center_lng: -30...
 
153
    max_lat: 52...
 
154
    max_lng: 0...
 
155
    min_lat: -43...
 
156
    min_lng: -62...
 
157
 
 
158
Calling getMappedParticipantsBounds() on a team without members is an error.
 
159
 
 
160
    >>> unmapped_team = factory.makeTeam()
 
161
    >>> unmapped_team.getMappedParticipantsBounds()
 
162
    Traceback (most recent call last):
 
163
     ...
 
164
    AssertionError: This method cannot be called when
 
165
                    mapped_participants_count == 0.
 
166
 
 
167
 
 
168
Location visibility
 
169
-------------------
 
170
 
 
171
Some people may not want their location to be disclosed to others, so
 
172
we provide a way for them to hide their location from other users. By
 
173
default a person's location is visible.
 
174
 
 
175
    >>> salgado = personset.getByName('salgado')
 
176
    >>> login_person(salgado)
 
177
    >>> salgado.setLocation(-43.0, -62.1, 'America/Sao_Paulo', salgado)
 
178
    >>> salgado.location.visible
 
179
    True
 
180
    >>> salgado.location.latitude
 
181
    -43...
 
182
 
 
183
    >>> login_person(jdub)
 
184
    >>> salgado.location.latitude
 
185
    -43...
 
186
 
 
187
But it can be changed through the setLocationVisibility() method. If the
 
188
visibility is set to False, only the person himself will be able to see
 
189
the location data except for time zone.
 
190
 
 
191
    >>> login_person(salgado)
 
192
    >>> salgado.setLocationVisibility(False)
 
193
    >>> salgado.location.visible
 
194
    False
 
195
 
 
196
    >>> login_person(jdub)
 
197
    >>> print salgado.time_zone
 
198
    America/Sao_Paulo
 
199
    >>> salgado.latitude
 
200
    Traceback (most recent call last):
 
201
    ...
 
202
    Unauthorized:...
 
203
    >>> salgado.longitude
 
204
    Traceback (most recent call last):
 
205
    ...
 
206
    Unauthorized:...
 
207
    >>> salgado.location.latitude
 
208
    Traceback (most recent call last):
 
209
    ...
 
210
    Unauthorized:...
 
211
 
 
212
A team's .mapped_participants will also exclude the members who made
 
213
their location invisible.
 
214
 
 
215
    >>> admins = personset.getByName('admins')
 
216
    >>> salgado in admins.activemembers
 
217
    True
 
218
    >>> salgado in admins.getMappedParticipants()
 
219
    False