Fixed bug in `Search().lookup_user()` now it's `self._connection.get()`
[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 """
dd0a4d9f
MM
27 data = {}
28 stream = []
17d8f406 29
7b99bf75 30 def __init__(self, connection, guid='', handle='', fetch='posts', id=0):
beaa09fb 31 self._connection = connection
b9fb4030
JR
32 self.data = {
33 'guid': guid,
34 'handle': handle,
1e192d06 35 'id': id,
b9fb4030
JR
36 }
37 self._do_fetch(fetch)
beaa09fb
MM
38
39 def __getitem__(self, key):
40 return self.data[key]
41
1e192d06
MM
42 def __str__(self):
43 return self['guid']
44
45 def __repr__(self):
46 return '{0} ({1})'.format(self['diaspora_name'], self['guid'])
47
b9fb4030
JR
48 def _do_fetch(self, fetch):
49 if fetch == 'posts':
1f779f83
JR
50 if self['handle'] and self['guid']: self.fetchguid()
51 elif self['guid'] and not self['handle']: self.fetchguid()
52 elif self['handle'] and not self['guid']: self.fetchhandle()
b9fb4030 53 elif fetch == 'data' and len(self['handle']):
1f779f83 54 self.fetchprofile()
b9fb4030 55
1f779f83 56 def _sephandle(self):
beaa09fb
MM
57 """Separate D* handle into pod pod and user.
58
beaa09fb
MM
59 :returns: two-tuple (pod, user)
60 """
1f779f83
JR
61 if re.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', self['handle']) is None:
62 raise Exception('invalid handle: {0}'.format(self['handle']))
63 handle = self['handle'].split('@')
beaa09fb
MM
64 pod, user = handle[1], handle[0]
65 return (pod, user)
3cf4514e 66
7b99bf75
JR
67 def _finalize_data(self, data, names):
68 final = {}
69 for d, f in names:
70 final[f] = data[d]
71 return final
beaa09fb 72
a8fdc14a
MM
73 def _postproc(self, request):
74 """Makes necessary modifications to user data and
75 sets up a stream.
76
77 :param request: request object
78 :type request: request
beaa09fb 79 """
141216df 80 if request.status_code != 200:
a8fdc14a 81 raise Exception('wrong error code: {0}'.format(request.status_code))
141216df
MM
82 else:
83 request = request.json()
65b1f099 84 if not len(request): raise errors.UserError('cannot extract user data: no posts to analyze')
a8fdc14a
MM
85 names = [('id', 'id'),
86 ('diaspora_id', 'diaspora_id'),
87 ('guid', 'guid'),
88 ('name', 'diaspora_name'),
89 ('avatar', 'image_urls'),
f605e88d 90 ]
7b99bf75 91 self.data = self._finalize_data(request[0]['author'], names)
1f779f83 92 self.stream = Outer(self._connection, location='people/{0}.json'.format(self['guid']))
beaa09fb 93
1f779f83 94 def fetchhandle(self, protocol='https'):
7b99bf75 95 """Fetch user data and posts using Diaspora handle.
beaa09fb 96 """
1f779f83 97 pod, user = self._sephandle()
a8fdc14a
MM
98 request = self._connection.session.get('{0}://{1}/u/{2}.json'.format(protocol, pod, user))
99 self._postproc(request)
beaa09fb 100
1f779f83 101 def fetchguid(self):
7b99bf75 102 """Fetch user data and posts using guid.
beaa09fb 103 """
1f779f83 104 request = self._connection.get('people/{0}.json'.format(self['guid']))
17d8f406 105 self._postproc(request)
3cf4514e 106
1f779f83 107 def fetchprofile(self, protocol='https'):
7b99bf75
JR
108 """Fetch user data using Diaspora handle.
109 """
1f779f83 110 request = self._connection.get('people.json?q={0}'.format(self['handle']))
7b99bf75
JR
111 if request.status_code != 200:
112 raise Exception('wrong error code: {0}'.format(request.status_code))
113 else:
114 request = request.json()
b9fb4030
JR
115 if len(request):
116 names = [('id', 'id'),
117 ('handle', 'diaspora_id'),
118 ('guid', 'guid'),
119 ('name', 'diaspora_name'),
120 ('avatar', 'image_urls'),
121 ]
122 self.data = self._finalize_data(request[0], names)
dd0a4d9f
MM
123
124
125class Contacts():
126 """This class represents user's list of contacts.
127 """
128 def __init__(self, connection):
129 self._connection = connection
130
d589deff
MM
131 def add(self, user_id, aspect_ids):
132 """Add user to aspects of given ids.
dd0a4d9f 133
d589deff
MM
134 :param user_id: user guid
135 :type user_id: str
136 :param aspect_ids: list of aspect ids
137 :type aspect_ids: list
dd0a4d9f 138 """
7a818fdb 139 for aid in aspect_ids: Aspect(self._connection, aid).addUser(user_id)
27f09973 140
d589deff
MM
141 def remove(self, user_id, aspect_ids):
142 """Remove user from aspects of given ids.
27f09973 143
d589deff
MM
144 :param user_id: user guid
145 :type user_id: str
146 :param aspect_ids: list of aspect ids
147 :type aspect_ids: list
27f09973 148 """
7a818fdb 149 for aid in aspect_ids: Aspect(self._connection, aid).removeUser(user_id)
27f09973 150
d589deff 151 def get(self, set=''):
27f09973 152 """Returns list of user contacts.
d589deff
MM
153 Contact is a User() who is in one or more of user's
154 aspects.
155
7a818fdb
MM
156 By default, it will return list of users who are in
157 user's aspects.
158
d589deff
MM
159 If `set` is `all` it will also include users who only share
160 with logged user and are not in his/hers aspects.
7a818fdb 161
d589deff
MM
162 If `set` is `only_sharing` it will return users who are only
163 sharing with logged user and ARE NOT in his/hers aspects.
164
165 :param set: if passed could be 'all' or 'only_sharing'
166 :type set: str
27f09973 167 """
d589deff
MM
168 params = {}
169 if set: params['set'] = set
170
171 request = self._connection.get('contacts.json', params=params)
27f09973
MM
172 if request.status_code != 200:
173 raise Exception('status code {0}: cannot get contacts'.format(request.status_code))
7b99bf75 174 contacts = [User(self._connection, user['guid'], user['handle'], 'none', user['id']) for user in request.json()]
27f09973 175 return contacts