1 # -*- coding: utf-8 -*-
3 """This module provides access to user's settings on Diaspora*.
13 from diaspy
import errors
, streams
17 """Provides interface to account settings.
19 email_regexp
= re
.compile('<input id="user_email" name="user\[email\]" size="30" type="text" value="(.+?)"')
20 language_option_regexp
= re
.compile('<option value="([-_a-z]+)">(.*?)</option>')
22 def __init__(self
, connection
):
23 self
._connection
= connection
25 def downloadxml(self
):
26 """Returns downloaded XML.
28 request
= self
._connection
.get('user/export')
31 def downloadPhotos(self
, size
='large', path
='.', mark_nsfw
=True, _critical
=False, _stream
=None):
32 """Downloads photos into the current working directory.
33 Sizes are: large, medium, small.
34 Filename is: {post_guid}_{photo_guid}.{extension}
36 Normally, this method will catch urllib-generated errors and
37 just issue warnings about photos that couldn't be downloaded.
38 However, with _critical param set to True errors will become
39 critical - the will be reraised in finally block.
41 :param size: size of the photos to download - large, medium or small
43 :param path: path to download (defaults to current working directory
45 :param mark_nsfw: will append '-nsfw' to images from posts marked as nsfw,
47 :param _stream: diaspy.streams.Generic-like object (only for testing)
48 :param _critical: if True urllib errors will be reraised after generating a warning (may be removed)
50 :returns: integer, number of photos downloaded
54 stream
= streams
.Activity(self
._connection
)
58 for i
, post
in enumerate(stream
):
59 if post
['nsfw'] is not False: nsfw
= '-nsfw'
62 for n
, photo
in enumerate(post
['photos']):
63 # photo format -- .jpg, .png etc.
64 ext
= photo
['sizes'][size
].split('.')[-1]
65 name
= '{0}_{1}{2}.{3}'.format(post
['guid'], photo
['guid'], nsfw
, ext
)
66 filename
= os
.path
.join(path
, name
)
68 urllib
.request
.urlretrieve(url
=photo
['sizes'][size
], filename
=filename
)
69 except (urllib
.error
.HTTPError
, urllib
.error
.URLError
) as e
:
70 warnings
.warn('downloading image {0} from post {1}: {2}'.format(photo
['guid'], post
['guid'], e
))
76 def setEmail(self
, email
):
77 """Changes user's email.
79 data
= {'_method': 'put', 'utf8': '✓', 'user[email]': email
, 'authenticity_token': repr(self
._connection
)}
80 request
= self
._connection
.post('user', data
=data
, allow_redirects
=False)
81 if request
.status_code
!= 302:
82 raise errors
.SettingsError('setting email failed: {0}'.format(request
.status_code
))
85 """Returns currently used email.
87 data
= self
._connection
.get('user/edit')
88 email
= self
.email_regexp
.search(data
.text
)
89 if email
is None: email
= ''
90 else: email
= email
.group(1)
93 def setLanguage(self
, lang
):
94 """Changes user's email.
96 :param lang: language identifier from getLanguages()
98 data
= {'_method': 'put', 'utf8': '✓', 'user[language]': lang
, 'authenticity_token': repr(self
._connection
)}
99 request
= self
._connection
.post('user', data
=data
, allow_redirects
=False)
100 if request
.status_code
!= 302:
101 raise errors
.SettingsError('setting language failed: {0}'.format(request
.status_code
))
103 def getLanguages(self
):
104 """Returns a list of tuples containing ('Language name', 'identifier').
105 One of the Black Magic(tm) methods.
107 request
= self
._connection
.get('user/edit')
108 return self
.language_option_regexp
.findall(request
.text
)
112 """Provides interface to provacy settings.
114 def __init__(self
, connection
):
115 self
._connection
= connection
119 """Provides interface to profile settigns.
123 Because of the way update requests for profile are created every field must be sent.
124 The `load()` method is used to load all information into the dictionary.
125 Setters can then be used to adjust the data.
126 Finally, `update()` can be called to send data back to pod.
128 firstname_regexp
= re
.compile('id="profile_first_name" name="profile\[first_name\]" type="text" value="(.*?)" />')
129 lastname_regexp
= re
.compile('id="profile_last_name" name="profile\[last_name\]" type="text" value="(.*?)" />')
130 bio_regexp
= re
.compile('<textarea id="profile_bio" name="profile\[bio\]" placeholder="Fill me out" rows="5">\n(.*?)</textarea>')
131 location_regexp
= re
.compile('id="profile_location" name="profile\[location\]" placeholder="Fill me out" type="text" value="(.*?)" />')
132 gender_regexp
= re
.compile('id="profile_gender" name="profile\[gender\]" placeholder="Fill me out" type="text" value="(.*?)" />')
133 birth_year_regexp
= re
.compile('selected="selected" value="([0-9]{4,4})">[0-9]{4,4}</option>')
134 birth_month_regexp
= re
.compile('selected="selected" value="([0-9]{1,2})">(.*?)</option>')
135 birth_day_regexp
= re
.compile('selected="selected" value="([0-9]{1,2})">[0-9]{1,2}</option>')
136 is_searchable_regexp
= re
.compile('checked="checked" id="profile_searchable" name="profile\[searchable\]" type="checkbox" value="(.*?)" />')
137 is_nsfw_regexp
= re
.compile('checked="checked" id="profile_nsfw" name="profile\[nsfw\]" type="checkbox" value="(.*?)" />')
139 def __init__(self
, connection
):
140 self
._connection
= connection
141 self
.data
= {'utf-8': '✓',
143 'profile[first_name]': '',
144 'profile[last_name]': '',
145 'profile[tag_string]': '',
149 'profile[location]': '',
150 'profile[gender]': '',
151 'profile[date][year]': '',
152 'profile[date][month]': '',
153 'profile[date][day]': '',
155 self
._html
= self
._fetchhtml
()
158 def _fetchhtml(self
):
159 """Fetches html that will be used to extract data.
161 return self
._connection
.get('profile/edit').text
164 """Returns two-tuple: (first, last) name.
166 first
= self
.firstname_regexp
.search(self
._html
).group(1)
167 last
= self
.lastname_regexp
.search(self
._html
).group(1)
171 """Returns tags user had selected when describing him/her-self.
173 guid
= self
._connection
.getUserData()['guid']
174 html
= self
._connection
.get('people/{0}'.format(guid
)).text
175 description_regexp
= re
.compile('<a href="/tags/(.*?)" class="tag">#.*?</a>')
176 return [tag
.lower() for tag
in re
.findall(description_regexp
, html
)]
181 bio
= self
.bio_regexp
.search(self
._html
).group(1)
184 def getLocation(self
):
185 """Returns location string.
187 location
= self
.location_regexp
.search(self
._html
).group(1)
191 """Returns location string.
193 gender
= self
.gender_regexp
.search(self
._html
).group(1)
196 def getBirthDate(self
, named_month
=False):
197 """Returns three-tuple: (year, month, day).
199 :param named_month: if True, return name of the month instead of integer
200 :type named_month: bool
202 year
= self
.birth_year_regexp
.search(self
._html
)
203 if year
is None: year
= -1
204 else: year
= int(year
.group(1))
205 month
= self
.birth_month_regexp
.search(self
._html
)
207 if named_month
: month
= ''
211 month
= month
.group(2)
213 month
= int(month
.group(1))
214 day
= self
.birth_day_regexp
.search(self
._html
)
215 if day
is None: day
= -1
216 else: day
= int(day
.group(1))
217 return (year
, month
, day
)
219 def isSearchable(self
):
220 """Returns True if profile is searchable.
222 searchable
= self
.is_searchable_regexp
.search(self
._html
)
223 # this is because value="true" in every case so we just
224 # check if the field is "checked"
225 if searchable
is None: searchable
= False # if it isn't - the regexp just won't match
226 else: searchable
= True
230 """Returns True if profile is marked as NSFW.
232 nsfw
= self
.is_nsfw_regexp
.search(self
._html
)
233 if nsfw
is None: nsfw
= False
237 def setName(self
, first
, last
):
238 """Set first and last name.
240 self
.data
['profile[first_name]'] = first
241 self
.data
['profile[last_name]'] = last
243 def setTags(self
, tags
):
244 """Sets tags that describe the user.
246 self
.data
['tags'] = ', '.join(['#{}'.format(tag
) for tag
in tags
])
248 def setBio(self
, bio
):
249 """Set bio of a user.
251 self
.data
['profile[bio]'] = bio
253 def setLocation(self
, location
):
254 """Set location of a user.
256 self
.data
['profile[location]'] = location
258 def setGender(self
, gender
):
259 """Set gender of a user.
261 self
.data
['profile[gender]'] = gender
263 def setBirthDate(self
, year
, month
, day
):
264 """Set birth date of a user.
266 self
.data
['profile[date][year]'] = year
267 self
.data
['profile[date][month]'] = month
268 self
.data
['profile[date][day]'] = day
270 def setSearchable(self
, searchable
):
271 """Set user's searchable status.
273 self
.data
['profile[searchable]'] = json
.dumps(searchable
)
275 def setNSFW(self
, nsfw
):
276 """Set user NSFW status.
278 self
.data
['profile[nsfw]'] = json
.dumps(nsfw
)
281 """Loads profile data into self.data dictionary.
282 **Notice:** Not all keys are loaded yet.
284 self
.setName(*self
.getName())
285 self
.setBio(self
.getBio())
286 self
.setLocation(self
.getLocation())
287 self
.setGender(self
.getGender())
288 self
.setBirthDate(*self
.getBirthDate(named_month
=False))
289 self
.setSearchable(self
.isSearchable())
290 self
.setNSFW(self
.isNSFW())
291 self
.setTags(self
.getTags())
295 """Updates profile information.
297 if not self
._loaded
: raise errors
.DiaspyError('profile was not loaded')
298 self
.data
['authenticity_token'] = repr(self
._connection
)
300 request
= self
._connection
.post('profile', data
=self
.data
, allow_redirects
=False)
301 return request
.status_code
305 """Provides interface to services settings.
307 def __init__(self
, connection
):
308 self
._connection
= connection