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