da6f183bfc96e7f8c56622c6e96c0d6cdfbc5bad
1 """This module provides access to user's settings on Diaspora*.
11 from diaspy
import errors
, streams
15 """Provides interface to account settings.
17 email_regexp
= re
.compile('<input id="user_email" name="user\[email\]" size="30" type="text" value="(.+?)"')
19 def __init__(self
, connection
):
20 self
._connection
= connection
22 def downloadxml(self
):
23 """Returns downloaded XML.
25 request
= self
._connection
.get('user/export')
28 def downloadPhotos(self
, size
='large', path
='.', mark_nsfw
=True, _critical
=False, _stream
=None):
29 """Downloads photos into the current working directory.
30 Sizes are: large, medium, small.
31 Filename is: {post_guid}_{photo_guid}.{extension}
33 Normally, this method will catch urllib-generated errors and
34 just issue warnings about photos that couldn't be downloaded.
35 However, with _critical param set to True errors will become
36 critical - the will be reraised in finally block.
38 :param size: size of the photos to download - large, medium or small
40 :param path: path to download (defaults to current working directory
42 :param mark_nsfw: will append '-nsfw' to images from posts marked as nsfw,
44 :param _stream: diaspy.streams.Generic-like object (only for testing)
45 :param _critical: if True urllib errors will be reraised after generating a warning (may be removed)
47 :returns: integer, number of photos downloaded
51 stream
= streams
.Activity(self
._connection
)
55 for i
, post
in enumerate(stream
):
56 if post
['nsfw'] is not False: nsfw
= '-nsfw'
59 for n
, photo
in enumerate(post
['photos']):
60 # photo format -- .jpg, .png etc.
61 ext
= photo
['sizes'][size
].split('.')[-1]
62 name
= '{0}_{1}{2}.{3}'.format(post
['guid'], photo
['guid'], nsfw
, ext
)
63 filename
= os
.path
.join(path
, name
)
65 urllib
.request
.urlretrieve(url
=photo
['sizes'][size
], filename
=filename
)
66 except (urllib
.error
.HTTPError
, urllib
.error
.URLError
) as e
:
67 warnings
.warn('downloading image {0} from post {1}: {2}'.format(photo
['guid'], post
['guid'], e
))
73 def setEmail(self
, email
):
74 """Changes user's email.
76 data
= {'_method': 'put', 'utf8': '✓', 'user[email]': email
, 'authenticity_token': repr(self
._connection
)}
77 request
= self
._connection
.post('user', data
=data
, allow_redirects
=False)
78 if request
.status_code
!= 302:
79 raise errors
.SettingsError('setting email failed: {0}'.format(request
.status_code
))
82 """Returns currently used email.
84 data
= self
._connection
.get('user/edit')
85 email
= self
.email_regexp
.search(data
.text
)
86 if email
is None: raise errors
.DiaspyError('cannot fetch email')
87 email
= email
.group(1)
90 def setLanguage(self
, lang
):
91 """Changes user's email.
93 :param lang: language identifier from getLanguages()
95 data
= {'_method': 'put', 'utf8': '✓', 'user[language]': lang
, 'authenticity_token': repr(self
._connection
)}
96 request
= self
._connection
.post('user', data
=data
, allow_redirects
=False)
97 return request
.status_code
99 def getLanguages(self
):
100 """Returns a list of tuples containing ('Language name', 'identifier').
101 One of the Black Magic(tm) methods.
103 selection_start
= '<select id="user_language" name="user[language]">'
104 selection_end
= '</select>'
106 request
= self
._connection
.get('user/edit')
107 data
= request
.text
[request
.text
.find(selection_start
)+len(selection_start
):]
108 data
= data
[:data
.find(selection_end
)].split('\n')
110 name
= item
[item
.find('>')+1:item
.rfind('<')]
111 identifier
= item
[item
.find('"')+1:]
112 identifier
= identifier
[:identifier
.find('"')]
113 languages
.append((name
, identifier
))
118 """Provides interface to provacy settings.
120 def __init__(self
, connection
):
121 self
._connection
= connection
125 """Provides interface to profile settigns.
129 Because of the way update requests for profile are created every field must be sent.
130 The `load()` method is used to load all information into the dictionary.
131 Setters can then be used to adjust the data.
132 Finally, `update()` can be called to send data back to pod.
134 firstname_regexp
= re
.compile('id="profile_first_name" name="profile\[first_name\]" type="text" value="(.*?)" />')
135 lastname_regexp
= re
.compile('id="profile_last_name" name="profile\[last_name\]" type="text" value="(.*?)" />')
136 bio_regexp
= re
.compile('<textarea id="profile_bio" name="profile\[bio\]" placeholder="Fill me out" rows="5">\n(.*?)</textarea>')
137 location_regexp
= re
.compile('id="profile_location" name="profile\[location\]" placeholder="Fill me out" type="text" value="(.*?)" />')
138 gender_regexp
= re
.compile('id="profile_gender" name="profile\[gender\]" placeholder="Fill me out" type="text" value="(.*?)" />')
139 birth_year_regexp
= re
.compile('selected="selected" value="([0-9]{4,4})">[0-9]{4,4}</option>')
140 birth_month_regexp
= re
.compile('selected="selected" value="([0-9]{1,2})">(.*?)</option>')
141 birth_day_regexp
= re
.compile('selected="selected" value="([0-9]{1,2})">[0-9]{1,2}</option>')
142 is_searchable_regexp
= re
.compile('checked="checked" id="profile_searchable" name="profile\[searchable\]" type="checkbox" value="(.*?)" />')
143 is_nsfw_regexp
= re
.compile('checked="checked" id="profile_nsfw" name="profile\[nsfw\]" type="checkbox" value="(.*?)" />')
145 def __init__(self
, connection
):
146 self
._connection
= connection
147 self
.data
= {'utf-8': '✓',
149 'profile[first_name]': '',
150 'profile[last_name]': '',
151 'profile[tag_string]': '',
155 'profile[location]': '',
156 'profile[gender]': '',
157 'profile[date][year]': '',
158 'profile[date][month]': '',
159 'profile[date][day]': '',
161 self
._html
= self
._fetchhtml
()
164 def _fetchhtml(self
):
165 """Fetches html that will be used to extract data.
167 return self
._connection
.get('profile/edit').text
170 """Returns two-tuple: (first, last) name.
172 first
= self
.firstname_regexp
.search(self
._html
).group(1)
173 last
= self
.lastname_regexp
.search(self
._html
).group(1)
177 """Returns tags user had selected when describing him/her-self.
179 guid
= self
._connection
.getUserInfo()['guid']
180 html
= self
._connection
.get('people/{0}'.format(guid
)).text
181 description_regexp
= re
.compile('<a href="/tags/(.*?)" class="tag">#.*?</a>')
182 return [tag
.lower() for tag
in re
.findall(description_regexp
, html
)]
187 bio
= self
.bio_regexp
.search(self
._html
).group(1)
190 def getLocation(self
):
191 """Returns location string.
193 location
= self
.location_regexp
.search(self
._html
).group(1)
197 """Returns location string.
199 gender
= self
.gender_regexp
.search(self
._html
).group(1)
202 def getBirthDate(self
, named_month
=False):
203 """Returns three-tuple: (year, month, day).
205 :param named_month: if True, return name of the month instead of integer
206 :type named_month: bool
208 year
= self
.birth_year_regexp
.search(self
._html
)
209 if year
is None: year
= -1
210 else: year
= int(year
.group(1))
211 month
= self
.birth_month_regexp
.search(self
._html
)
213 if named_month
: month
= ''
217 month
= month
.group(2)
219 month
= int(month
.group(1))
220 day
= self
.birth_day_regexp
.search(self
._html
)
221 if day
is None: day
= -1
222 else: day
= int(day
.group(1))
223 return (year
, month
, day
)
225 def isSearchable(self
):
226 """Returns True if profile is searchable.
228 searchable
= self
.is_searchable_regexp
.search(self
._html
)
229 # this is because value="true" in every case so we just
230 # check if the field is "checked"
231 if searchable
is None: searchable
= False # if it isn't - the regexp just won't match
232 else: searchable
= True
236 """Returns True if profile is marked as NSFW.
238 nsfw
= self
.is_nsfw_regexp
.search(self
._html
)
239 if nsfw
is None: nsfw
= False
243 def setName(self
, first
, last
):
244 """Set first and last name.
246 self
.data
['profile[first_name]'] = first
247 self
.data
['profile[last_name]'] = last
249 def setTags(self
, tags
):
250 """Sets tags that describe the user.
252 self
.data
['tags'] = ', '.join(['#{}'.format(tag
) for tag
in tags
])
254 def setBio(self
, bio
):
255 """Set bio of a user.
257 self
.data
['profile[bio]'] = bio
259 def setLocation(self
, location
):
260 """Set location of a user.
262 self
.data
['profile[location]'] = location
264 def setGender(self
, gender
):
265 """Set gender of a user.
267 self
.data
['profile[gender]'] = gender
269 def setBirthDate(self
, year
, month
, day
):
270 """Set birth date of a user.
272 self
.data
['profile[date][year]'] = year
273 self
.data
['profile[date][month]'] = month
274 self
.data
['profile[date][day]'] = day
276 def setSearchable(self
, searchable
):
277 """Set user's searchable status.
279 self
.data
['profile[searchable]'] = json
.dumps(searchable
)
281 def setNSFW(self
, nsfw
):
282 """Set user NSFW status.
284 self
.data
['profile[nsfw]'] = json
.dumps(nsfw
)
287 """Loads profile data into self.data dictionary.
288 **Notice:** Not all keys are loaded yet.
290 self
.setName(*self
.getName())
291 self
.setBio(self
.getBio())
292 self
.setLocation(self
.getLocation())
293 self
.setGender(self
.getGender())
294 self
.setBirthDate(*self
.getBirthDate(named_month
=False))
295 self
.setSearchable(self
.isSearchable())
296 self
.setNSFW(self
.isNSFW())
297 self
.setTags(self
.getTags())
301 """Updates profile information.
303 if not self
._loaded
: raise errors
.DiaspyError('profile was not loaded')
304 self
.data
['authenticity_token'] = repr(self
._connection
)
306 request
= self
._connection
.post('profile', data
=self
.data
, allow_redirects
=False)
307 return request
.status_code
311 """Provides interface to services settings.
313 def __init__(self
, connection
):
314 self
._connection
= connection