cc4b0a448f2539b587178c36b169c38aead08367
[diaspy.git] / diaspy / models.py
1 #!/usr/bin/env python3
2
3
4 import json
5 import re
6
7
8 """This module is only imported in other diaspy modules and
9 MUST NOT import anything.
10 """
11
12
13 class Aspect():
14 """This class represents an aspect.
15 """
16 def __init__(self, connection, id=-1):
17 self._connection = connection
18 self.id = id
19 self.name = self._findname()
20
21 def _findname(self):
22 """Finds name for aspect.
23 """
24 name = ''
25 aspects = self._connection.getUserInfo()['aspects']
26 for a in aspects:
27 if a['id'] == self.id:
28 name = a['name']
29 break
30 return name
31
32 def getUsers(self):
33 """Returns list of GUIDs of users who are listed in this aspect.
34 """
35 start_regexp = re.compile('<ul +class=["\']contacts["\'] *>')
36 userline_regexp = re.compile('<a href=["\']/people/[a-z0-9]{16,16}["\']>[a-zA-Z0-9 _-]+</a>')
37 personid_regexp = 'alt="{0}" class="avatar" data-person_id="[0-9]+"'
38 method_regexp = 'data-method="delete" data-person_id="{0}"'
39
40 ajax = self._connection.get('aspects/{0}/edit'.format(self.id)).text
41 begin = ajax.find(start_regexp.search(ajax).group(0))
42 end = ajax.find('</ul>')
43 ajax = ajax[begin:end]
44
45 usernames = [(line[17:33], line[35:-4]) for line in userline_regexp.findall(ajax)]
46 personids = [re.compile(personid_regexp.format(name)).search(ajax).group(0) for guid, name in usernames]
47 for n, line in enumerate(personids):
48 i, id = -2, ''
49 while line[i].isdigit():
50 id = line[i] + id
51 i -= 1
52 personids[n] = (usernames[n][1], id)
53
54 users_in_aspect = []
55 for name, id in personids:
56 if re.compile(method_regexp.format(id, self.id)).search(ajax): users_in_aspect.append(name)
57
58 users = []
59 for i, user in enumerate(usernames):
60 guid, name = user
61 if name in users_in_aspect:
62 users.append(guid)
63 return users
64
65 def addUser(self, user_id):
66 """Add user to current aspect.
67
68 :param user_id: user to add to aspect
69 :type user: int
70 """
71 data = {'authenticity_token': self._connection.get_token(),
72 'aspect_id': self.id,
73 'person_id': user_id}
74
75 request = self._connection.post('aspect_memberships.json', data=data)
76
77 if request.status_code == 400:
78 raise Exception('duplicate record, user already exists in aspect: {0}'.format(request.status_code))
79 elif request.status_code == 404:
80 raise Exception('user not found from this pod: {0}'.format(request.status_code))
81 elif request.status_code != 200:
82 raise Exception('wrong status code: {0}'.format(request.status_code))
83 return request.json()
84
85 def removeUser(self, user_id):
86 """Remove user from current aspect.
87
88 :param user_id: user to remove from aspect
89 :type user: int
90 """
91 data = {'authenticity_token': self._connection.get_token(),
92 'aspect_id': self.id,
93 'person_id': user_id}
94
95 request = self.connection.delete('aspect_memberships/{0}.json'.format(self.id), data=data)
96
97 if request.status_code != 200:
98 raise Exception('wrong status code: {0}'.format(request.status_code))
99 return request.json()
100
101
102 class Notification():
103 """This class represents single notification.
104 """
105 _who_regexp = re.compile(r'/people/[0-9a-z]+" class=\'hovercardable')
106 _when_regexp = re.compile(r'[0-9]{4,4}(-[0-9]{2,2}){2,2} [0-9]{2,2}(:[0-9]{2,2}){2,2} UTC')
107
108 def __init__(self, connection, data):
109 self._connection = connection
110 self.type = list(data.keys())[0]
111 self.data = data[self.type]
112 self.id = self.data['id']
113 self.unread = self.data['unread']
114
115 def __getitem__(self, key):
116 """Returns a key from notification data.
117 """
118 return self.data[key]
119
120 def __str__(self):
121 """Returns notification note.
122 """
123 string = re.sub('</?[a-z]+( *[a-z_-]+=["\'][\w():.,!?#/\- ]*["\'])* */?>', '', self.data['note_html'])
124 string = string.strip().split('\n')[0]
125 while ' ' in string: string = string.replace(' ', ' ')
126 return string
127
128 def __repr__(self):
129 """Returns notification note with more details.
130 """
131 return '{0}: {1}'.format(self.when(), str(self))
132
133 def who(self):
134 """Returns list of guids of the users who caused you to get the notification.
135 """
136 return [who[8:24] for who in self._who_regexp.findall(self.data['note_html'])]
137
138 def when(self):
139 """Returns UTC time as found in note_html.
140 """
141 return self._when_regexp.search(self.data['note_html']).group(0)
142
143 def mark(self, unread=False):
144 """Marks notification to read/unread.
145 Marks notification to read if `unread` is False.
146 Marks notification to unread if `unread` is True.
147
148 :param unread: which state set for notification
149 :type unread: bool
150 """
151 headers = {'x-csrf-token': self._connection.get_token()}
152 params = {'set_unread': json.dumps(unread)}
153 self._connection.put('notifications/{0}'.format(self['id']), params=params, headers=headers)
154 self.data['unread'] = unread
155
156
157 class Post():
158 """This class represents a post.
159
160 .. note::
161 Remember that you need to have access to the post.
162 """
163 def __init__(self, post_id, connection):
164 """
165 :param post_id: id or guid of the post
166 :type post_id: str
167 :param connection: connection object used to authenticate
168 :type connection: connection.Connection
169 """
170 self._connection = connection
171 self.post_id = post_id
172
173 def __repr__(self):
174 """Returns string containing more information then str().
175 """
176 data = self.get_data()
177 return '{0} ({1}): {2}'.format(data['author']['name'], data['author']['diaspora_id'], data['text'])
178
179 def __str__(self):
180 """Returns text of a post.
181 """
182 return self.get_data()['text']
183
184 def get_data(self):
185 """This function retrieves data of the post.
186 """
187 r = self._connection.get('posts/{0}.json'.format(self.post_id))
188 if r.status_code != 200:
189 raise Exception('wrong status code: {0}'.format(r.status_code))
190 return r.json()
191
192 def like(self):
193 """This function likes a post.
194 It abstracts the 'Like' functionality.
195
196 :returns: dict -- json formatted like object.
197 """
198 data = {'authenticity_token': self._connection.get_token()}
199
200 r = self._connection.post('posts/{0}/likes'.format(self.post_id),
201 data=data,
202 headers={'accept': 'application/json'})
203
204 if r.status_code != 201:
205 raise Exception('{0}: Post could not be liked.'
206 .format(r.status_code))
207
208 return r.json()
209
210 def delete_like(self):
211 """This function removes a like from a post
212 """
213 data = {'authenticity_token': self._connection.get_token()}
214
215 post_data = self.get_data()
216
217 r = self._connection.delete('posts/{0}/likes/{1}'
218 .format(self.post_id,
219 post_data['interactions']
220 ['likes'][0]['id']),
221 data=data)
222
223 if r.status_code != 204:
224 raise Exception('{0}: Like could not be removed.'
225 .format(r.status_code))
226
227 def reshare(self):
228 """This function reshares a post
229
230 """
231 post_data = self.get_data()
232
233 data = {'root_guid': post_data['guid'],
234 'authenticity_token': self._connection.get_token()}
235
236 r = self._connection.post('reshares',
237 data=data,
238 headers={'accept': 'application/json'})
239
240 if r.status_code != 201:
241 raise Exception('{0}: Post could not be reshared.'
242 .format(r.status_code))
243
244 return r.json()
245
246 def comment(self, text):
247 """This function comments on a post
248
249 :param text: text to comment.
250 :type text: str
251 """
252 data = {'text': text,
253 'authenticity_token': self._connection.get_token()}
254
255 r = self._connection.post('posts/{0}/comments'.format(self.post_id),
256 data=data,
257 headers={'accept': 'application/json'})
258
259 if r.status_code != 201:
260 raise Exception('{0}: Comment could not be posted.'
261 .format(r.status_code))
262
263 return r.json()
264
265 def delete_comment(self, comment_id):
266 """This function removes a comment from a post
267
268 :param comment_id: id of the comment to remove.
269 :type comment_id: str
270 """
271 data = {'authenticity_token': self._connection.get_token()}
272
273 r = self._connection.delete('posts/{0}/comments/{1}'
274 .format(self.post_id,
275 comment_id),
276 data=data,
277 headers={'accept': 'application/json'})
278
279 if r.status_code != 204:
280 raise Exception('{0}: Comment could not be deleted.'
281 .format(r.status_code))
282
283 def delete(self):
284 """ This function deletes this post
285 """
286 data = {'authenticity_token': self._connection.get_token()}
287 r = self._connection.delete('posts/{0}'.format(self.post_id),
288 data=data,
289 headers={'accept': 'application/json'})
290 if r.status_code != 204:
291 raise Exception('{0}: Post could not be deleted'.format(r.status_code))