Changed fetching languages in diaspy/settings.py, setLanguage() now
[diaspy.git] / diaspy / settings.py
index 27cd904663deab1464500911f70316a1fd2c72ba..ded22589228a3ebdb5a6beb3a9b53e370cd5e402 100644 (file)
@@ -11,19 +11,128 @@ import warnings
 from diaspy import errors, streams
 
 
+class Account():
+    """Provides interface to account settings.
+    """
+    email_regexp = re.compile('<input id="user_email" name="user\[email\]" size="30" type="text" value="(.+?)"')
+    language_option_regexp = re.compile('<option value="([-_a-z]+)">(.*?)</option>')
+
+    def __init__(self, connection):
+        self._connection = connection
+
+    def downloadxml(self):
+        """Returns downloaded XML.
+        """
+        request = self._connection.get('user/export')
+        return request.text
+
+    def downloadPhotos(self, size='large', path='.', mark_nsfw=True, _critical=False, _stream=None):
+        """Downloads photos into the current working directory.
+        Sizes are: large, medium, small.
+        Filename is: {post_guid}_{photo_guid}.{extension}
+
+        Normally, this method will catch urllib-generated errors and
+        just issue warnings about photos that couldn't be downloaded.
+        However, with _critical param set to True errors will become
+        critical - the will be reraised in finally block.
+
+        :param size: size of the photos to download - large, medium or small
+        :type size: str
+        :param path: path to download (defaults to current working directory
+        :type path: str
+        :param mark_nsfw: will append '-nsfw' to images from posts marked as nsfw,
+        :type mark_nsfw: bool
+        :param _stream: diaspy.streams.Generic-like object (only for testing)
+        :param _critical: if True urllib errors will be reraised after generating a warning (may be removed)
+
+        :returns: integer, number of photos downloaded
+        """
+        photos = 0
+        if _stream is None:
+            stream = streams.Activity(self._connection)
+            stream.full()
+        else:
+            stream = _stream
+        for i, post in enumerate(stream):
+            if post['nsfw'] is not False: nsfw = '-nsfw'
+            else: nsfw = ''
+            if post['photos']:
+                for n, photo in enumerate(post['photos']):
+                    # photo format -- .jpg, .png etc.
+                    ext = photo['sizes'][size].split('.')[-1]
+                    name = '{0}_{1}{2}.{3}'.format(post['guid'], photo['guid'], nsfw, ext)
+                    filename = os.path.join(path, name)
+                    try:
+                        urllib.request.urlretrieve(url=photo['sizes'][size], filename=filename)
+                    except (urllib.error.HTTPError, urllib.error.URLError) as e:
+                        warnings.warn('downloading image {0} from post {1}: {2}'.format(photo['guid'], post['guid'], e))
+                    finally:
+                        if _critical: raise
+                photos += 1
+        return photos
+
+    def setEmail(self, email):
+        """Changes user's email.
+        """
+        data = {'_method': 'put', 'utf8': '✓', 'user[email]': email, 'authenticity_token': repr(self._connection)}
+        request = self._connection.post('user', data=data, allow_redirects=False)
+        if request.status_code != 302:
+            raise errors.SettingsError('setting email failed: {0}'.format(request.status_code))
+
+    def getEmail(self):
+        """Returns currently used email.
+        """
+        data = self._connection.get('user/edit')
+        email = self.email_regexp.search(data.text)
+        if email is None: email = ''
+        else: email = email.group(1)
+        return email
+
+    def setLanguage(self, lang):
+        """Changes user's email.
+
+        :param lang: language identifier from getLanguages()
+        """
+        data = {'_method': 'put', 'utf8': '✓', 'user[language]': lang, 'authenticity_token': repr(self._connection)}
+        request = self._connection.post('user', data=data, allow_redirects=False)
+        if request.status_code != 302:
+            raise errors.SettingsError('setting language failed: {0}'.format(request.status_code))
+
+    def getLanguages(self):
+        """Returns a list of tuples containing ('Language name', 'identifier').
+        One of the Black Magic(tm) methods.
+        """
+        request = self._connection.get('user/edit')
+        return self.language_option_regexp.findall(request.text)
+
+
+class Privacy():
+    """Provides interface to provacy settings.
+    """
+    def __init__(self, connection):
+        self._connection = connection
+
+
 class Profile():
-    """Provides profile editing methods.
+    """Provides interface to profile settigns.
+
+    WARNING:
+
+        Because of the way update requests for profile are created every field must be sent.
+        The `load()` method is used to load all information into the dictionary.
+        Setters can then be used to adjust the data.
+        Finally, `update()` can be called to send data back to pod.
     """
-    firstname_regexp = re.compile('<input id="profile_first_name" name="profile\[first_name\]" type="text" value="(.*?)" />')
-    lastname_regexp = re.compile('<input id="profile_last_name" name="profile\[last_name\]" type="text" value="(.*?)" />')
+    firstname_regexp = re.compile('id="profile_first_name" name="profile\[first_name\]" type="text" value="(.*?)" />')
+    lastname_regexp = re.compile('id="profile_last_name" name="profile\[last_name\]" type="text" value="(.*?)" />')
     bio_regexp = re.compile('<textarea id="profile_bio" name="profile\[bio\]" placeholder="Fill me out" rows="5">\n(.*?)</textarea>')
-    location_regexp = re.compile('<input id="profile_location" name="profile\[location\]" placeholder="Fill me out" type="text" value="(.*?)" />')
-    gender_regexp = re.compile('<input id="profile_gender" name="profile\[gender\]" placeholder="Fill me out" type="text" value="(.*?)" />')
-    birth_year_regexp = re.compile('<option selected="selected" value="([0-9]{4,4})">[0-9]{4,4}</option>')
-    birth_month_regexp = re.compile('<option selected="selected" value="([0-9]{1,2})">(.*?)</option>')
-    birth_day_regexp = re.compile('<option selected="selected" value="([0-9]{1,2})">[0-9]{1,2}</option>')
-    is_searchable_regexp = re.compile('<input checked="checked" id="profile_searchable" name="profile\[searchable\]" type="checkbox" value="(.*?)" />')
-    is_nsfw_regexp = re.compile('<input checked="checked" id="profile_nsfw" name="profile\[nsfw\]" type="checkbox" value="(.*?)" />')
+    location_regexp = re.compile('id="profile_location" name="profile\[location\]" placeholder="Fill me out" type="text" value="(.*?)" />')
+    gender_regexp = re.compile('id="profile_gender" name="profile\[gender\]" placeholder="Fill me out" type="text" value="(.*?)" />')
+    birth_year_regexp = re.compile('selected="selected" value="([0-9]{4,4})">[0-9]{4,4}</option>')
+    birth_month_regexp = re.compile('selected="selected" value="([0-9]{1,2})">(.*?)</option>')
+    birth_day_regexp = re.compile('selected="selected" value="([0-9]{1,2})">[0-9]{1,2}</option>')
+    is_searchable_regexp = re.compile('checked="checked" id="profile_searchable" name="profile\[searchable\]" type="checkbox" value="(.*?)" />')
+    is_nsfw_regexp = re.compile('checked="checked" id="profile_nsfw" name="profile\[nsfw\]" type="checkbox" value="(.*?)" />')
 
     def __init__(self, connection):
         self._connection = connection
@@ -49,28 +158,6 @@ class Profile():
         """
         return self._connection.get('profile/edit').text
 
-    def load(self):
-        """Loads profile data into self.data dictionary.
-        **Notice:** Not all keys are loaded yet.
-        """
-        self.data['profile[first_name]'], self.data['profile[last_name]'] = self.getName()
-        self.data['profile[bio]'] = self.getBio()
-        self.data['profile[location]'] = self.getLocation()
-        self.data['profile[gender]'] = self.getGender()
-        year, month, day = self.getBirthDate(named_month=False)
-        self.data['profile[date][]'] = year
-        self.data['profile[date][]'] = month
-        self.data['profile[date][]'] = day
-        self._loaded = True
-
-    def update(self):
-        """Updates profile information.
-        """
-        if not self._loaded: raise errors.DiaspyError('profile was not loaded')
-        data['authenticity_token'] = repr(self._connection)
-        request = self._connection.post('profile', data=json.dumps(data), allow_redirects=False)
-        return request.status_code
-
     def getName(self):
         """Returns two-tuple: (first, last) name.
         """
@@ -145,101 +232,75 @@ class Profile():
         else: nsfw = True
         return nsfw
 
+    def setName(self, first, last):
+        """Set first and last name.
+        """
+        self.data['profile[first_name]'] = first
+        self.data['profile[last_name]'] = last
 
-class Account():
-    """This object is used to get access to user's settings on
-    Diaspora* and provides interface for downloading user's stuff.
-    """
-    def __init__(self, connection):
-        self._connection = connection
-    
-    def downloadxml(self):
-        """Returns downloaded XML.
+    def setTags(self, tags):
+        """Sets tags that describe the user.
         """
-        request = self._connection.get('user/export')
-        return request.text
+        self.data['tags'] = ', '.join(['#{}'.format(tag) for tag in tags])
 
-    def downloadPhotos(self, size='large', path='.', mark_nsfw=True, _critical=False, _stream=None):
-        """Downloads photos into the current working directory.
-        Sizes are: large, medium, small.
-        Filename is: {post_guid}_{photo_guid}.{extension}
+    def setBio(self, bio):
+        """Set bio of a user.
+        """
+        self.data['profile[bio]'] = bio
 
-        Normally, this method will catch urllib-generated errors and
-        just issue warnings about photos that couldn't be downloaded.
-        However, with _critical param set to True errors will become
-        critical - the will be reraised in finally block.
+    def setLocation(self, location):
+        """Set location of a user.
+        """
+        self.data['profile[location]'] = location
 
-        :param size: size of the photos to download - large, medium or small
-        :type size: str
-        :param path: path to download (defaults to current working directory
-        :type path: str
-        :param mark_nsfw: will append '-nsfw' to images from posts marked as nsfw,
-        :type mark_nsfw: bool
-        :param _stream: diaspy.streams.Generic-like object (only for testing)
-        :param _critical: if True urllib errors will be reraised after generating a warning (may be removed)
+    def setGender(self, gender):
+        """Set gender of a user.
+        """
+        self.data['profile[gender]'] = gender
 
-        :returns: integer, number of photos downloaded
+    def setBirthDate(self, year, month, day):
+        """Set birth date of a user.
         """
-        photos = 0
-        if _stream is None:
-            stream = streams.Activity(self._connection)
-            stream.full()
-        else:
-            stream = _stream
-        for i, post in enumerate(stream):
-            if post['nsfw'] is not False: nsfw = '-nsfw'
-            else: nsfw = ''
-            if post['photos']:
-                for n, photo in enumerate(post['photos']):
-                    name = '{0}_{1}{2}.{3}'.format(post['guid'], photo['guid'], nsfw, photo['sizes'][size].split('.')[-1])
-                    filename = os.path.join(path, name)
-                    try:
-                        urllib.request.urlretrieve(url=photo['sizes'][size], filename=filename)
-                    except (urllib.error.HTTPError, urllib.error.URLError) as e:
-                        warnings.warn('downloading image {0} from post {1}: {2}'.format(photo['guid'], post['guid'], e))
-                    finally:
-                        if _critical: raise
-                photos += 1
-        return photos
+        self.data['profile[date][year]'] = year
+        self.data['profile[date][month]'] = month
+        self.data['profile[date][day]'] = day
 
-    def setEmail(self, email):
-        """Changes user's email.
+    def setSearchable(self, searchable):
+        """Set user's searchable status.
         """
-        data = {'_method': 'put', 'utf8': '✓', 'user[email]': email, 'authenticity_token': repr(self._connection)}
-        request = self._connection.post('user', data=data, allow_redirects=False)
+        self.data['profile[searchable]'] = json.dumps(searchable)
 
-    def getEmail(self):
-        """Returns currently used email.
+    def setNSFW(self, nsfw):
+        """Set user NSFW status.
         """
-        data = self._connection.get('user/edit')
-        email = re.compile('<input id="user_email" name="user\[email\]" size="30" type="text" value=".+?"').search(data.text)
-        if email is None: raise errors.DiaspyError('cannot fetch email')
-        email = email.group(0)[:-1]
-        email = email[email.rfind('"')+1:]
-        return email
+        self.data['profile[nsfw]'] = json.dumps(nsfw)
 
-    def setLanguage(self, lang):
-        """Changes user's email.
+    def load(self):
+        """Loads profile data into self.data dictionary.
+        **Notice:** Not all keys are loaded yet.
+        """
+        self.setName(*self.getName())
+        self.setBio(self.getBio())
+        self.setLocation(self.getLocation())
+        self.setGender(self.getGender())
+        self.setBirthDate(*self.getBirthDate(named_month=False))
+        self.setSearchable(self.isSearchable())
+        self.setNSFW(self.isNSFW())
+        self.setTags(self.getTags())
+        self._loaded = True
 
-        :param lang: language identifier from getLanguages()
+    def update(self):
+        """Updates profile information.
         """
-        data = {'_method': 'put', 'utf8': '✓', 'user[language]': lang, 'authenticity_token': repr(self._connection)}
-        request = self._connection.post('user', data=data, allow_redirects=False)
+        if not self._loaded: raise errors.DiaspyError('profile was not loaded')
+        self.data['authenticity_token'] = repr(self._connection)
+        print(self.data)
+        request = self._connection.post('profile', data=self.data, allow_redirects=False)
         return request.status_code
 
-    def getLanguages(self):
-        """Returns a list of tuples containing ('Language name', 'identifier').
-        One of the Black Magic(tm) methods.
-        """
-        selection_start = '<select id="user_language" name="user[language]">'
-        selection_end = '</select>'
-        languages = []
-        request = self._connection.get('user/edit')
-        data = request.text[request.text.find(selection_start)+len(selection_start):]
-        data = data[:data.find(selection_end)].split('\n')
-        for item in data:
-            name = item[item.find('>')+1:item.rfind('<')]
-            identifier = item[item.find('"')+1:]
-            identifier = identifier[:identifier.find('"')]
-            languages.append((name, identifier))
-        return languages
+
+class Services():
+    """Provides interface to services settings.
+    """
+    def __init__(self, connection):
+        self._connection = connection