Fixed possible bug related to having multiple instances of
[diaspy.git] / diaspy / people.py
CommitLineData
beaa09fb
MM
1import re
2from diaspy.streams import Outer
d589deff 3from diaspy.models import Aspect
65b1f099 4from diaspy import errors
beaa09fb
MM
5
6
488d7ff6 7class User():
beaa09fb
MM
8 """This class abstracts a D* user.
9 This object goes around the limitations of current D* API and will
10 extract user data using black magic.
11 However, no chickens are harmed when you use it.
17d8f406 12
7b99bf75
JR
13 The parameter fetch should be either 'posts', 'data' or 'none'. By
14 default it is 'posts' which means in addition to user data, stream
3cf4514e
MM
15 will be fetched. If user has not posted yet diaspy will not be able
16 to extract the information from his/her posts. Since there is no official
17 way to do it we rely on user posts. If this will be the case user
7b99bf75 18 will be notified with appropriate exception message.
3cf4514e 19
7b99bf75
JR
20 If fetch is 'data', only user data will be fetched. If the user is
21 not found, no exception will be returned.
22
23 When creating new User() one can pass either guid, handle and/or id as
24 optional parameters. GUID takes precedence over handle when fetching
25 user stream. When fetching user data, handle is required.
beaa09fb 26 """
7b99bf75 27 def __init__(self, connection, guid='', handle='', fetch='posts', id=0):
beaa09fb 28 self._connection = connection
6cd1bae0 29 self.stream = []
b9fb4030
JR
30 self.data = {
31 'guid': guid,
32 'handle': handle,
1e192d06 33 'id': id,
b9fb4030
JR
34 }
35 self._do_fetch(fetch)
beaa09fb
MM
36
37 def __getitem__(self, key):
38 return self.data[key]
39
1e192d06
MM
40 def __str__(self):
41 return self['guid']
42
43 def __repr__(self):
44 return '{0} ({1})'.format(self['diaspora_name'], self['guid'])
45
b9fb4030
JR
46 def _do_fetch(self, fetch):
47 if fetch == 'posts':
1f779f83
JR
48 if self['handle'] and self['guid']: self.fetchguid()
49 elif self['guid'] and not self['handle']: self.fetchguid()
50 elif self['handle'] and not self['guid']: self.fetchhandle()
b9fb4030 51 elif fetch == 'data' and len(self['handle']):
1f779f83 52 self.fetchprofile()
b9fb4030 53
1f779f83 54 def _sephandle(self):
beaa09fb
MM
55 """Separate D* handle into pod pod and user.
56
beaa09fb
MM
57 :returns: two-tuple (pod, user)
58 """
1f779f83
JR
59 if re.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', self['handle']) is None:
60 raise Exception('invalid handle: {0}'.format(self['handle']))
61 handle = self['handle'].split('@')
beaa09fb
MM
62 pod, user = handle[1], handle[0]
63 return (pod, user)
3cf4514e 64
7b99bf75
JR
65 def _finalize_data(self, data, names):
66 final = {}
67 for d, f in names:
68 final[f] = data[d]
69 return final
beaa09fb 70
a8fdc14a
MM
71 def _postproc(self, request):
72 """Makes necessary modifications to user data and
73 sets up a stream.
74
75 :param request: request object
76 :type request: request
beaa09fb 77 """
141216df 78 if request.status_code != 200:
a8fdc14a 79 raise Exception('wrong error code: {0}'.format(request.status_code))
141216df
MM
80 else:
81 request = request.json()
65b1f099 82 if not len(request): raise errors.UserError('cannot extract user data: no posts to analyze')
a8fdc14a
MM
83 names = [('id', 'id'),
84 ('diaspora_id', 'diaspora_id'),
85 ('guid', 'guid'),
86 ('name', 'diaspora_name'),
87 ('avatar', 'image_urls'),
f605e88d 88 ]
7b99bf75 89 self.data = self._finalize_data(request[0]['author'], names)
1f779f83 90 self.stream = Outer(self._connection, location='people/{0}.json'.format(self['guid']))
beaa09fb 91
1f779f83 92 def fetchhandle(self, protocol='https'):
7b99bf75 93 """Fetch user data and posts using Diaspora handle.
beaa09fb 94 """
1f779f83 95 pod, user = self._sephandle()
a8fdc14a
MM
96 request = self._connection.session.get('{0}://{1}/u/{2}.json'.format(protocol, pod, user))
97 self._postproc(request)
beaa09fb 98
1f779f83 99 def fetchguid(self):
7b99bf75 100 """Fetch user data and posts using guid.
beaa09fb 101 """
1f779f83 102 request = self._connection.get('people/{0}.json'.format(self['guid']))
17d8f406 103 self._postproc(request)
3cf4514e 104
1f779f83 105 def fetchprofile(self, protocol='https'):
7b99bf75
JR
106 """Fetch user data using Diaspora handle.
107 """
1f779f83 108 request = self._connection.get('people.json?q={0}'.format(self['handle']))
7b99bf75
JR
109 if request.status_code != 200:
110 raise Exception('wrong error code: {0}'.format(request.status_code))
111 else:
112 request = request.json()
b9fb4030
JR
113 if len(request):
114 names = [('id', 'id'),
115 ('handle', 'diaspora_id'),
116 ('guid', 'guid'),
117 ('name', 'diaspora_name'),
118 ('avatar', 'image_urls'),
119 ]
120 self.data = self._finalize_data(request[0], names)
dd0a4d9f
MM
121
122
123class Contacts():
124 """This class represents user's list of contacts.
125 """
126 def __init__(self, connection):
127 self._connection = connection
128
d589deff
MM
129 def add(self, user_id, aspect_ids):
130 """Add user to aspects of given ids.
dd0a4d9f 131
d589deff
MM
132 :param user_id: user guid
133 :type user_id: str
134 :param aspect_ids: list of aspect ids
135 :type aspect_ids: list
dd0a4d9f 136 """
7a818fdb 137 for aid in aspect_ids: Aspect(self._connection, aid).addUser(user_id)
27f09973 138
d589deff
MM
139 def remove(self, user_id, aspect_ids):
140 """Remove user from aspects of given ids.
27f09973 141
d589deff
MM
142 :param user_id: user guid
143 :type user_id: str
144 :param aspect_ids: list of aspect ids
145 :type aspect_ids: list
27f09973 146 """
7a818fdb 147 for aid in aspect_ids: Aspect(self._connection, aid).removeUser(user_id)
27f09973 148
d589deff 149 def get(self, set=''):
27f09973 150 """Returns list of user contacts.
d589deff
MM
151 Contact is a User() who is in one or more of user's
152 aspects.
153
7a818fdb
MM
154 By default, it will return list of users who are in
155 user's aspects.
156
d589deff
MM
157 If `set` is `all` it will also include users who only share
158 with logged user and are not in his/hers aspects.
7a818fdb 159
d589deff
MM
160 If `set` is `only_sharing` it will return users who are only
161 sharing with logged user and ARE NOT in his/hers aspects.
162
163 :param set: if passed could be 'all' or 'only_sharing'
164 :type set: str
27f09973 165 """
d589deff
MM
166 params = {}
167 if set: params['set'] = set
168
169 request = self._connection.get('contacts.json', params=params)
27f09973
MM
170 if request.status_code != 200:
171 raise Exception('status code {0}: cannot get contacts'.format(request.status_code))
7b99bf75 172 contacts = [User(self._connection, user['guid'], user['handle'], 'none', user['id']) for user in request.json()]
27f09973 173 return contacts