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-zA-Z-]+)"(?: selected="selected")?>(.*?)</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
, no_load
=False):
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
()
157 if not no_load
: self
.load()
159 def _fetchhtml(self
):
160 """Fetches html that will be used to extract data.
162 return self
._connection
.get('profile/edit').text
165 """Returns two-tuple: (first, last) name.
167 first
= self
.firstname_regexp
.search(self
._html
).group(1)
168 last
= self
.lastname_regexp
.search(self
._html
).group(1)
172 """Returns tags user had selected when describing him/her-self.
174 guid
= self
._connection
.getUserData()['guid']
175 html
= self
._connection
.get('people/{0}'.format(guid
)).text
176 description_regexp
= re
.compile('<a href="/tags/(.*?)" class="tag">#.*?</a>')
177 return [tag
.lower() for tag
in re
.findall(description_regexp
, html
)]
182 bio
= self
.bio_regexp
.search(self
._html
).group(1)
185 def getLocation(self
):
186 """Returns location string.
188 location
= self
.location_regexp
.search(self
._html
).group(1)
192 """Returns location string.
194 gender
= self
.gender_regexp
.search(self
._html
).group(1)
197 def getBirthDate(self
, named_month
=False):
198 """Returns three-tuple: (year, month, day).
200 :param named_month: if True, return name of the month instead of integer
201 :type named_month: bool
203 year
= self
.birth_year_regexp
.search(self
._html
)
204 if year
is None: year
= -1
205 else: year
= int(year
.group(1))
206 month
= self
.birth_month_regexp
.search(self
._html
)
208 if named_month
: month
= ''
212 month
= month
.group(2)
214 month
= int(month
.group(1))
215 day
= self
.birth_day_regexp
.search(self
._html
)
216 if day
is None: day
= -1
217 else: day
= int(day
.group(1))
218 return (year
, month
, day
)
220 def isSearchable(self
):
221 """Returns True if profile is searchable.
223 searchable
= self
.is_searchable_regexp
.search(self
._html
)
224 # this is because value="true" in every case so we just
225 # check if the field is "checked"
226 if searchable
is None: searchable
= False # if it isn't - the regexp just won't match
227 else: searchable
= True
231 """Returns True if profile is marked as NSFW.
233 nsfw
= self
.is_nsfw_regexp
.search(self
._html
)
234 if nsfw
is None: nsfw
= False
238 def setName(self
, first
, last
):
239 """Set first and last name.
241 self
.data
['profile[first_name]'] = first
242 self
.data
['profile[last_name]'] = last
244 def setTags(self
, tags
):
245 """Sets tags that describe the user.
247 self
.data
['tags'] = ', '.join(['#{}'.format(tag
) for tag
in tags
])
249 def setBio(self
, bio
):
250 """Set bio of a user.
252 self
.data
['profile[bio]'] = bio
254 def setLocation(self
, location
):
255 """Set location of a user.
257 self
.data
['profile[location]'] = location
259 def setGender(self
, gender
):
260 """Set gender of a user.
262 self
.data
['profile[gender]'] = gender
264 def setBirthDate(self
, year
, month
, day
):
265 """Set birth date of a user.
267 self
.data
['profile[date][year]'] = year
268 self
.data
['profile[date][month]'] = month
269 self
.data
['profile[date][day]'] = day
271 def setSearchable(self
, searchable
):
272 """Set user's searchable status.
274 self
.data
['profile[searchable]'] = json
.dumps(searchable
)
276 def setNSFW(self
, nsfw
):
277 """Set user NSFW status.
279 self
.data
['profile[nsfw]'] = json
.dumps(nsfw
)
282 """Loads profile data into self.data dictionary.
283 **Notice:** Not all keys are loaded yet.
285 self
.setName(*self
.getName())
286 self
.setBio(self
.getBio())
287 self
.setLocation(self
.getLocation())
288 self
.setGender(self
.getGender())
289 self
.setBirthDate(*self
.getBirthDate(named_month
=False))
290 self
.setSearchable(self
.isSearchable())
291 self
.setNSFW(self
.isNSFW())
292 self
.setTags(self
.getTags())
296 """Updates profile information.
298 if not self
._loaded
: raise errors
.DiaspyError('profile was not loaded')
299 self
.data
['authenticity_token'] = repr(self
._connection
)
301 request
= self
._connection
.post('profile', data
=self
.data
, allow_redirects
=False)
302 return request
.status_code
306 """Provides interface to services settings.
308 def __init__(self
, connection
):
309 self
._connection
= connection