import re
from diaspy.streams import Outer
+from diaspy.models import Aspect
+from diaspy import errors
+from diaspy import search
-class User:
+class User():
"""This class abstracts a D* user.
This object goes around the limitations of current D* API and will
extract user data using black magic.
However, no chickens are harmed when you use it.
- If user has not posted yet diaspy will not be able to extract the information
- from his/her posts. Since there is no official way to do it we rely
- on user posts. If this will be the case user will be notified with appropriate
- exception message.
+ The parameter fetch should be either 'posts', 'data' or 'none'. By
+ default it is 'posts' which means in addition to user data, stream
+ will be fetched. If user has not posted yet diaspy will not be able
+ to extract the information from his/her posts. Since there is no official
+ way to do it we rely on user posts. If this will be the case user
+ will be notified with appropriate exception message.
- When creating new User() one can pass either guid or handle as
- an optional parameter. GUID takes precedence over handle.
- """
- data = {}
- stream = []
+ If fetch is 'data', only user data will be fetched. If the user is
+ not found, no exception will be returned.
- def __init__(self, connection, guid='', handle=''):
+ When creating new User() one can pass either guid, handle and/or id as
+ optional parameters. GUID takes precedence over handle when fetching
+ user stream. When fetching user data, handle is required.
+ """
+ def __init__(self, connection, guid='', handle='', fetch='posts', id=0):
self._connection = connection
- self.guid, self.handle = guid, handle
- if handle and guid: self.fetchguid(guid)
- elif guid and not handle: self.fetchguid(guid)
- elif handle and not guid: self.fetchhandle(handle)
+ self.stream = []
+ self.data = {
+ 'guid': guid,
+ 'handle': handle,
+ 'id': id,
+ }
+ self._do_fetch(fetch)
def __getitem__(self, key):
return self.data[key]
- def _sephandle(self, handle):
+ def __str__(self):
+ return self['guid']
+
+ def __repr__(self):
+ return '{0} ({1})'.format(self['diaspora_name'], self['guid'])
+
+ def _do_fetch(self, fetch):
+ if fetch == 'posts':
+ if self['handle'] and self['guid']: self.fetchguid()
+ elif self['guid'] and not self['handle']: self.fetchguid()
+ elif self['handle'] and not self['guid']: self.fetchhandle()
+ elif fetch == 'data' and len(self['handle']):
+ self._postproc(search.Search(self._connection).users(query=handle)[0])
+
+ def _sephandle(self):
"""Separate D* handle into pod pod and user.
- :param handle: diaspora id: user@pod.example.com
- :type handle: str
:returns: two-tuple (pod, user)
"""
- if re.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', handle) is None:
- raise Exception('invalid handle: {0}'.format(handle))
- handle = handle.split('@')
+ if re.match('^[a-zA-Z]+[a-zA-Z0-9_-]*@[a-z0-9.]+\.[a-z]+$', self['handle']) is None:
+ raise Exception('invalid handle: {0}'.format(self['handle']))
+ handle = self['handle'].split('@')
pod, user = handle[1], handle[0]
return (pod, user)
+ def _finalize_data(self, data):
+ names = [('id', 'id'),
+ ('handle', 'diaspora_id'),
+ ('guid', 'guid'),
+ ('name', 'diaspora_name'),
+ ('avatar', 'image_urls'),
+ ]
+ final = {}
+ for d, f in names:
+ final[f] = data[d]
+ return final
+
def _postproc(self, request):
"""Makes necessary modifications to user data and
sets up a stream.
raise Exception('wrong error code: {0}'.format(request.status_code))
else:
request = request.json()
+ if not len(request): raise errors.UserError('cannot extract user data: no posts to analyze')
+ self.data = self._finalize_data(request[0]['author'])
+ self.stream = Outer(self._connection, location='people/{0}.json'.format(self['guid']))
- if not len(request): raise ('Cannot extract user data: no posts to analyze')
- data = request[0]['author']
- final = {}
- names = [('id', 'id'),
- ('diaspora_id', 'diaspora_id'),
- ('guid', 'guid'),
- ('name', 'diaspora_name'),
- ('avatar', 'image_urls'),
- ]
- for d, f in names:
- final[f] = data[d]
- self.data = final
- self.stream = Outer(self._connection, location='people/{0}.json'.format(self.data['guid']))
-
- def fetchhandle(self, diaspora_id, protocol='https'):
- """Fetch user data using Diaspora handle.
+ def fetchhandle(self, protocol='https'):
+ """Fetch user data and posts using Diaspora handle.
"""
- pod, user = self._sephandle(diaspora_id)
+ pod, user = self._sephandle()
request = self._connection.session.get('{0}://{1}/u/{2}.json'.format(protocol, pod, user))
self._postproc(request)
- def fetchguid(self, guid):
- """Fetch user data using guid.
+ def fetchguid(self):
+ """Fetch user data and posts using guid.
"""
- request = self._connection.get('people/{0}.json'.format(guid))
+ request = self._connection.get('people/{0}.json'.format(self['guid']))
self._postproc(request)
def __init__(self, connection):
self._connection = connection
- def get_only_sharing(self):
- """Returns contacts who are only sharing with you.
+ def add(self, user_id, aspect_ids):
+ """Add user to aspects of given ids.
- :returns: list
+ :param user_id: user guid
+ :type user_id: str
+ :param aspect_ids: list of aspect ids
+ :type aspect_ids: list
"""
- params = {'set': 'only_sharing'}
- request = self._connection.get('contacts.json', params=params)
- if request.status_code != 200:
- raise Exception('status code {0}: cannot get contacts'.format(request.status_code))
- contacts = [User(user['guid']) for user in request.json()]
- return contacts
+ for aid in aspect_ids: Aspect(self._connection, aid).addUser(user_id)
- def get_all(self):
- """Returns list of all contacts.
+ def remove(self, user_id, aspect_ids):
+ """Remove user from aspects of given ids.
- :returns: list
+ :param user_id: user guid
+ :type user_id: str
+ :param aspect_ids: list of aspect ids
+ :type aspect_ids: list
"""
- params = {'set': 'all'}
- request = self._connection.get('contacts.json', params=params)
- if request.status_code != 200:
- raise Exception('status code {0}: cannot get contacts'.format(request.status_code))
- contacts = [User(user['guid']) for user in request.json()]
- return contacts
+ for aid in aspect_ids: Aspect(self._connection, aid).removeUser(user_id)
- def get(self):
+ def get(self, set=''):
"""Returns list of user contacts.
+ Contact is a User() who is in one or more of user's
+ aspects.
+
+ By default, it will return list of users who are in
+ user's aspects.
+
+ If `set` is `all` it will also include users who only share
+ with logged user and are not in his/hers aspects.
+
+ If `set` is `only_sharing` it will return users who are only
+ sharing with logged user and ARE NOT in his/hers aspects.
+
+ :param set: if passed could be 'all' or 'only_sharing'
+ :type set: str
"""
- request = self._connection.get('contacts.json')
+ params = {}
+ if set: params['set'] = set
+
+ request = self._connection.get('contacts.json', params=params)
if request.status_code != 200:
raise Exception('status code {0}: cannot get contacts'.format(request.status_code))
- contacts = [User(user['guid']) for user in request.json()]
+ contacts = [User(self._connection, user['guid'], user['handle'], 'none', user['id']) for user in request.json()]
return contacts