Please review, I hope the other devs will approve to these changes.
connection.py
- Set self._userdata on getUserData().
errors.py
- Added SearchError and TagError.
models.py
- Added removeAspect() to class Aspect().
- Set self.id and self.guid on Notification() class.
- Created class Comments() instead of the list.
- Post() can now be set with post data from a response without fetching the post again.
- Post() now returns a Comment() object instead of json when commented.
- Update Post()'s self._data['interactions']['likes'] after liked.
- Added some interaction functions for class Post(): vote_poll( poll_answer_id ), hide(), mute(), subscribe(), unsubscribe(). -- report() is still TODO
notifications.py
- I made some mistake again with updating the notification values, so hopefully fixed now.
people.py
- Able to manually set User()'s data.
- Remove aspect id from User() object when removed.
- Made User()'s handle() little more flexible and replaced self['handle'] with self.handle().
- Added getPhotos() and deletePhoto() functions to User() class.
- getHCard() didn't seem so work anymore so tweaked it a little, should be good now.
- Class Contacts() it's now optional to set fetch param.
- Contacts() now has functionality to add and delete Aspects.
- Contacts()'s get() should now return all contacts if param `set` is set.
streams.py
- Fetching more posts seemed to miss some posts, hoped it's fixed now ( But it will require dateutil or pytz ).
- Posts where fetched twice.
- Removed _obtain() from Outer().
- Updated Aspect()'s filter function.
"""
return self._diaspora_session
+ def userdata(self):
+ return self._userdata
+
def getUserData(self):
"""Returns user data.
"""
if userdata is None: userdata = self._userinfo_regex_2.search(request.text)
if userdata is None: raise errors.DiaspyError('cannot find user data')
userdata = userdata.group(1)
- return json.loads(userdata)
+ self._userdata = json.loads(userdata)
+ return self._userdata
def set_verify_SSL(self, verify):
"""Sets whether there should be an error if a SSL-Certificate could not be verified.
"""
pass
+class SearchError(DiaspyError):
+ """Exception raised when something related to searching goes wrong.
+ """
+ pass
+
+class TagError(DiaspyError):
+ """Exception raised when something related to settings goes wrong.
+ """
+ pass
def react(r, message='', accepted=[200, 201, 202, 203, 204, 205, 206], exception=DiaspyError):
"""This method tries to decide how to react
from diaspy import errors
-
class Aspect():
"""This class represents an aspect.
self._cached = request.json()
return self._cached
+ def removeAspect(self):
+ """
+ --> POST /aspects/{id} HTTP/1.1
+ --> _method=delete&authenticity_token={token}
+
+ <-- HTTP/1.1 302 Found
+
+ TODO: status_codes
+
+ Removes whole aspect.
+ :returns: None
+ """
+ request = self._connection.tokenFrom('contacts').delete('aspects/{}'.format(self.id))
+
+ if request.status_code != 302:
+ raise errors.AspectError('wrong status code: {0}'.format(request.status_code))
+
def addUser(self, user_id):
"""Add user to current aspect.
:param user_id: user to add to aspect
:type user_id: int
:returns: JSON from request
-
+
--> POST /aspect_memberships HTTP/1.1
--> Accept: application/json, text/javascript, */*; q=0.01
--> Content-Type: application/json; charset=UTF-8
--> {"aspect_id":123,"person_id":123}
-
+
<-- HTTP/1.1 200 OK
"""
data = {
if response is None:
raise errors.CSRFProtectionKickedIn()
+ # Now you should fetchguid(fetch_stream=False) on User to update aspect membership_id's
+ # Or update it locally with the response
return response
def removeUser(self, user):
:type user: diaspy.people.User object
"""
membership_id = None
+ to_remove = None
for each in user.aspectMemberships():
print(self.id, each)
if each.get('aspect', {}).get('id') == self.id:
membership_id = each.get('id')
+ to_remove = each
break # no need to continue
if membership_id is None:
elif request.status_code != 200:
raise errors.AspectError('cannot remove user from aspect: {0}'.format(request.status_code))
+ if 'contact' in user.data: # User object
+ if to_remove: user.data['contact']['aspect_memberships'].remove( to_remove ) # remove local aspect membership_id
+ else: # User object from Contacts()
+ if to_remove: user.data['aspect_memberships'].remove( to_remove ) # remove local aspect membership_id
return request.json()
"""
def __init__(self, data):
self._data = data
+ self.id = data['id']
+ self.guid = data['guid']
def __str__(self):
"""Returns comment's text.
"""
return self._data['author'][key]
+class Comments():
+ def __init__(self, comments=None):
+ self._comments = comments
+
+ def __iter__(self):
+ if self._comments:
+ for comment in self._comments:
+ yield comment
+
+ def __len__(self):
+ if self._comments:
+ return len(self._comments)
+
+ def __getitem__(self, index):
+ if self._comments:
+ return self._comments[index]
+
+ def __bool__(self):
+ if self._comments:
+ return True
+ return False
+
+ def ids(self):
+ return [c.id for c in self._comments]
+
+ def add(self, comment):
+ """ Expects comment object
+ TODO self._comments is None sometimes, have to look into it."""
+ if comment and self._comments:
+ self._comments.append(comment)
+
+ def set(self, comments):
+ """Sets comments wich already have a Comment obj"""
+ if comments:
+ self._comments = comments
+
+ def set_json(self, json_comments):
+ """Sets comments for this post from post data."""
+ if json_comments:
+ self._comments = [Comment(c) for c in json_comments]
class Post():
"""This class represents a post.
.. note::
Remember that you need to have access to the post.
"""
- def __init__(self, connection, id=0, guid='', fetch=True, comments=True):
+ def __init__(self, connection, id=0, guid='', fetch=True, comments=True, post_data=None):
"""
:param id: id of the post (GUID is recommended)
:type id: int
:type fetch: bool
:param comments: defines whether to fetch post's comments or not (if True also data will be fetched)
:type comments: bool
+ :param post_data: contains post data so no need to fetch the post if this is set, until you want to update post data
+ :type: json
"""
if not (guid or id): raise TypeError('neither guid nor id was provided')
self._connection = connection
self.id = id
self.guid = guid
self._data = {}
- self.comments = []
+ self.comments = Comments()
+ if post_data:
+ self._data = post_data
+
if fetch: self._fetchdata()
if comments:
if not self._data: self._fetchdata()
self._fetchcomments()
+ else:
+ if not self._data: self._fetchdata()
+ self.comments.set_json( self['interactions']['comments'] )
def __repr__(self):
"""Returns string containing more information then str().
request = self._connection.get('posts/{0}.json'.format(id))
if request.status_code != 200:
raise errors.PostError('{0}: could not fetch data for post: {1}'.format(request.status_code, id))
- else:
+ elif request:
self._data = request.json()
return self['guid']
if request.status_code != 200:
raise errors.PostError('{0}: could not fetch comments for post: {1}'.format(request.status_code, id))
else:
- self.comments = [Comment(c) for c in request.json()]
+ self.comments.set([Comment(c) for c in request.json()])
def update(self):
"""Updates post data.
"""
data = {'authenticity_token': repr(self._connection)}
- request = self._connection.post('posts/{0}/likes'.format(self.id),
+ request = self._connection.post('posts/{0}/likes'.format(self.id),
data=data,
headers={'accept': 'application/json'})
if request.status_code != 201:
raise errors.PostError('{0}: Post could not be liked.'
.format(request.status_code))
- return request.json()
+
+ likes_json = request.json()
+ if likes_json:
+ self._data['interactions']['likes'] = [likes_json]
+ return likes_json
def reshare(self):
"""This function reshares a post
if request.status_code != 201:
raise Exception('{0}: Comment could not be posted.'
.format(request.status_code))
+ return Comment(request.json())
+
+ def vote_poll(self, poll_answer_id):
+ """This function votes on a post's poll
+
+ :param poll_answer_id: id to poll vote.
+ :type poll_answer_id: int
+ """
+ poll_id = self._data['poll']['poll_id']
+ data = {'poll_answer_id': poll_answer_id,
+ 'poll_id': poll_id,
+ 'post_id': self.id,
+ 'authenticity_token': repr(self._connection)}
+ request = self._connection.post('posts/{0}/poll_participations'.format(self.id),
+ data=data,
+ headers={'accept': 'application/json'})
+ if request.status_code != 201:
+ raise Exception('{0}: Vote on poll failed.'
+ .format(request.status_code))
return request.json()
+ def hide(self):
+ """
+ -> PUT /share_visibilities/42 HTTP/1.1
+ post_id=123
+ <- HTTP/1.1 200 OK
+ """
+ headers = {'x-csrf-token': repr(self._connection)}
+ params = {'post_id': json.dumps(self.id)}
+ request = self._connection.put('share_visibilities/42', params=params, headers=headers)
+ if request.status_code != 200:
+ raise Exception('{0}: Failed to hide post.'
+ .format(request.status_code))
+
+ def mute(self):
+ """
+ -> POST /blocks HTTP/1.1
+ {"block":{"person_id":123}}
+ <- HTTP/1.1 204 No Content
+ """
+ headers = {'content-type':'application/json', 'x-csrf-token': repr(self._connection)}
+ data = json.dumps({ 'block': { 'person_id' : self._data['author']['id'] } })
+ request = self._connection.post('blocks', data=data, headers=headers)
+ if request.status_code != 204:
+ raise Exception('{0}: Failed to block person'
+ .format(request.status_code))
+
+ def subscribe(self):
+ """
+ -> POST /posts/123/participation HTTP/1.1
+ <- HTTP/1.1 201 Created
+ """
+ headers = {'x-csrf-token': repr(self._connection)}
+ data = {}
+ request = self._connection.post('posts/{}/participation'
+ .format( self.id ), data=data, headers=headers)
+ if request.status_code != 201:
+ raise Exception('{0}: Failed to subscribe to post'
+ .format(request.status_code))
+
+ def unsubscribe(self):
+ """
+ -> POST /posts/123/participation HTTP/1.1
+ _method=delete
+ <- HTTP/1.1 200 OK
+ """
+ headers = {'x-csrf-token': repr(self._connection)}
+ data = { "_method": "delete" }
+ request = self._connection.post('posts/{}/participation'
+ .format( self.id ), headers=headers, data=data)
+ if request.status_code != 200:
+ raise Exception('{0}: Failed to unsubscribe to post'
+ .format(request.status_code))
+
+ def report(self):
+ """
+ TODO
+ """
+ pass
+
def delete(self):
""" This function deletes this post
"""
if n.id not in ids:
if n.unread:
data['unread_count'] +=1
- data['unread_count_by_type'][n.type] +=1
+ data['unread_count_by_type'][n.type] +=1
notifications.append(n)
ids.append(n.id)
self._notifications = notifications
self._data = data
-
+
def _update(self, new_notifications):
ids = [notification.id for notification in self._notifications]
notifications = self._notifications
data = self._data
-
+
update = False
- if new_notifications[len(new_notifications)].id not in ids:
+ if new_notifications[len(new_notifications)-1].id not in ids:
update = True
-
+
for i in range(len(new_notifications)):
if new_notifications[-i].id not in ids:
if new_notifications[-i].unread:
- data[new_notifications[-i].type].unread_count +=1
- data[new_notifications[-i].type].unread_count_by_type +=1
+ data['unread_count'] +=1
+ data['unread_count_by_type'][new_notifications[-i].type] +=1
notifications = [new_notifications[-i]] + notifications
ids.append(new_notifications[-i].id)
self._notifications = notifications
self._data = data
- if update:
- self.update() # if there is a gap
+ if update: self.update() # if there is a gap
def update(self, per_page=5, page=1):
result = self.get(per_page=per_page, page=page)
- if result:
- self._expand( result )
-
+ if result: self._update( result )
+
def more(self, per_page=5, page=0):
if not page: page = self.page + 1
self.page = page
import json
import re
import warnings
+import time
from diaspy.streams import Outer
from diaspy.models import Aspect
if person_id is None:
raise errors.KeyMissingFromFetchedData('id', person)
- return User(connection, guid, handle, id)
+ return User(connection, guid, handle, id, data=data)
- def __init__(self, connection, guid='', handle='', fetch='posts', id=0):
+ def __init__(self, connection, guid='', handle='', fetch='posts', id=0, data=None):
self._connection = connection
self.stream = []
self.data = {
'handle': handle,
'id': id,
}
- self._fetch(fetch)
+ self.photos = []
+ if data: self.data.update( data )
+ if fetch: self._fetch(fetch)
def __getitem__(self, key):
return self.data[key]
return '{0} ({1})'.format(self.handle(), self.guid())
def handle(self):
+ if 'handle' in self.data: return self['handle']
return self.data.get('diaspora_id', 'Unknown handle')
def guid(self):
"""Fetch user posts or data.
"""
if fetch == 'posts':
- if self['handle'] and not self['guid']: self.fetchhandle()
+ if self.handle() and not self['guid']: self.fetchhandle()
else: self.fetchguid()
elif fetch == 'data' and self['handle']:
self.fetchprofile()
def fetchprofile(self):
"""Fetches user data.
- """
- data = search.Search(self._connection).user(self['handle'])
+ """
+ data = search.Search(self._connection).user(self.handle())
if not data:
- raise errors.UserError('user with handle "{0}" has not been found on pod "{1}"'.format(self['handle'], self._connection.pod))
+ raise errors.UserError('user with handle "{0}" has not been found on pod "{1}"'.format(self.handle(), self._connection.pod))
else:
- self.data = data[0]
+ self.data.update( data[0] )
def aspectMemberships(self):
- return self.data.get('contact', {}).get('aspect_memberships', [])
+ if 'contact' in self.data:
+ return self.data.get('contact', {}).get('aspect_memberships', [])
+ else:
+ return self.data.get('aspect_memberships', [])
+
+ def getPhotos(self):
+ """
+ --> GET /people/{GUID}/photos.json HTTP/1.1
+
+ <-- HTTP/1.1 200 OK
+
+ {
+ "photos":[
+ {
+ "id":{photo_id},
+ "guid":"{photo_guid}",
+ "created_at":"2018-03-08T23:48:31.000Z",
+ "author":{
+ "id":{author_id},
+ "guid":"{author_guid}",
+ "name":"{author_name}",
+ "diaspora_id":"{diaspora_id}",
+ "avatar":{"small":"{avatar_url_small}","medium":"{avatar_url_medium}","large":"{avatar_url_large}"}
+ },
+ "sizes":{
+ "small":"{photo_url}",
+ "medium":"{photo_url}",
+ "large":"{photo_url}"
+ },
+ "dimensions":{"height":847,"width":998},
+ "status_message":{
+ "id":{post_id}
+ }
+ },{ ..
+ }
+
+ if there are no photo's it returns:
+ {"photos":[]}
+ """
+
+ request = self._connection.get('/people/{0}/photos.json'.format(self['guid']))
+ if request.status_code != 200: raise errors.UserError('could not fetch photos for user: {0}'.format(self['guid']))
+
+ json = request.json()
+ if json: self.photos = json['photos']
+ return json['photos']
def getHCard(self):
- """Returns XML string containing user HCard.
+ """Returns json containing user HCard.
+ --> /people/{guid}/hovercard.json?_={timestamp}
+
+ <-- HTTP/2.0 200 OK
+ {
+ "id":123,
+ "guid":"1234567890abcdef",
+ "name":"test",
+ "diaspora_id":"batman@test.test",
+ "contact":false,
+ "profile":{
+ "avatar":"https://nicetesturl.url/image.jpg",
+ "tags":["tag1", "tag2", "tag3", "tag4", "tag5"]}
+ }
"""
- request = self._connection.get('hcard/users/{0}'.format(self['guid']))
+ timestamp = int(time.mktime(time.gmtime()))
+ request = self._connection.get('/people/{0}/hovercard.json?_={}'.format(self['guid'], timestamp))
if request.status_code != 200: raise errors.UserError('could not fetch hcard for user: {0}'.format(self['guid']))
- return request.text
+ return request.json()
+ def deletePhoto(self, photo_id):
+ """
+ --> DELETE /photos/{PHOTO_ID} HTTP/1.1
+ <-- HTTP/1.1 204 No Content
+ """
+ request = self._connection.delete('/photos/{0}'.format(photo_id))
+ if request.status_code != 204: raise errors.UserError('could not delete photo_id: {0}'.format(photo_id))
class Me():
"""Object represetnting current user.
class Contacts():
"""This class represents user's list of contacts.
"""
- def __init__(self, connection):
+ def __init__(self, connection, fetch=False, set=''):
self._connection = connection
+ self.contacts = None
+ if fetch: self.contacts = self.get(set)
+
+ def __getitem__(self, index):
+ return self.contacts[index]
+
+ def addAspect(self, name, visible=False):
+ """
+ --> POST /aspects HTTP/1.1
+ --> {"person_id":null,"name":"test","contacts_visible":false}
+
+ <-- HTTP/1.1 200 OK
+
+ Add new aspect.
+
+ TODO: status_code's
+
+ :param name: aspect name to add
+ :type name: str
+ :param visible: sets if contacts in aspect are visible for each and other
+ :type visible: bool
+ :returns: JSON from request
+ """
+ data = {
+ 'person_id': None,
+ 'name': name,
+ 'contacts_visible': visible
+ }
+ headers={'content-type': 'application/json',
+ 'accept': 'application/json' }
+ request = self._connection.tokenFrom('contacts').post('aspects', headers=headers, data=json.dumps(data))
+
+ if request.status_code == 400:
+ raise errors.AspectError('duplicate record, aspect alreadt exists: {0}'.format(request.status_code))
+ elif request.status_code != 200:
+ raise errors.AspectError('wrong status code: {0}'.format(request.status_code))
+
+ new_aspect = request.json()
+ self._connection.userdata()['aspects'].append( new_aspect )
+
+ return new_aspect
+
+ def deleteAspect(self, aspect_id):
+ """
+ --> POST /aspects/{ASPECT_ID} HTTP/1.1
+ _method=delete&authenticity_token={TOKEN}
+ Content-Type: application/x-www-form-urlencoded
+
+ <-- HTTP/1.1 302 Found
+ Content-Type: text/html; charset=utf-8
+ """
+ request = self._connection.tokenFrom('contacts').delete('aspects/{}'.format( aspect_id ))
+
+ if request.status_code != 200: # since we don't post but delete
+ raise errors.AspectError('wrong status code: {0}'.format(request.status_code))
def add(self, user_id, aspect_ids):
"""Add user to aspects of given ids.
- :param user_id: user guid
+ :param user_id: user id (not guid)
:type user_id: str
:param aspect_ids: list of aspect ids
:type aspect_ids: list
"""
- for aid in aspect_ids: Aspect(self._connection, aid).addUser(user_id)
+ # TODO update self.contacts
+ # Returns {"aspect_id":123,"person_id":123}
+ for aid in aspect_ids:
+ new_aspect_membership = Aspect(self._connection, aid).addUser(user_id)
+
+ # user.
+ if new_aspect_membership:
+ for user in self.contacts:
+ if int(user.data['person_id']) == int(user_id):
+ user.data['aspect_memberships'].append( new_aspect_membership )
+ return new_aspect_membership
def remove(self, user_id, aspect_ids):
"""Remove user from aspects of given ids.
- :param user_id: user guid
+ :param user_id: user id
:type user_id: str
:param aspect_ids: list of aspect ids
:type aspect_ids: list
"""
for aid in aspect_ids: Aspect(self._connection, aid).removeUser(user_id)
- def get(self, set=''):
+ def get(self, set='', page=0):
"""Returns list of user contacts.
Contact is a User() who is in one or more of user's
aspects.
If `set` is `only_sharing` it will return users who are only
sharing with logged user and ARE NOT in his/hers aspects.
+ # On "All contacts" button diaspora
+ on the time of testing this I had 20 contacts and 10 that
+ where only sharing with me. So 30 in total.
+
+ --> GET /contacts?set=all HTTP/1.1
+ <-- HTTP/1.1 200 OK
+ returned 25 contacts (5 only sharing with me)
+
+ --> GET /contacts.json?page=1&set=all&_=1524410225376 HTTP/1.1
+ <-- HTTP/1.1 200 OK
+ returned the same list as before.
+
+ --> GET /contacts.json?page=2&set=all&_=1524410225377 HTTP/1.1
+ <-- HTTP/1.1 200 OK
+ returned the other 5 that where only sharing with me.
+
+ --> GET /contacts.json?page=3&set=all&_=1524410225378 HTTP/1.1
+ <-- HTTP/1.1 200 OK
+ returned empty list.
+
+ It appears that /contacts?set=all returns a maximum of 25
+ contacts.
+
+ So if /contacts?set=all returns 25 contacts then request next
+ page until page returns a list with less then 25. I don't see a
+ reason why we should request page=1 'cause the previous request
+ will be the same. So begin with page=2 if /contacts?set=all
+ returns 25.
+
:param set: if passed could be 'all' or 'only_sharing'
:type set: str
"""
params = {}
- if set: params['set'] = set
+ if set:
+ params['set'] = set
+ params['_'] = int(time.mktime(time.gmtime()))
+ if page: params['page'] = page
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))
- return [User.parse(self._connection, each) for each in request.json()]
+
+ json = request.json()
+ users = [User.parse(self._connection, each) for each in json]
+ if len(json) == 25:
+ if not page: page = 1
+ users += self.get(set=set, page=page+1)
+ return users
from diaspy.models import Post, Aspect
from diaspy import errors
+"""
+Remember created_at is in UTC so I found two options to
+convert/parse it to UTC timestamp: dateutil or pytz (found some
+more but those libs aren't in default repo of main distro's)
+
+We need this to get a UTC timestamp from the latest loaded post in the
+stream, so we can use it for the more() function.
+"""
+try:
+ import dateutil.parser
+ def parse_utc_timestamp(date_str):
+ return round(dateutil.parser.parse(date_str).timestamp())
+
+except ImportError:
+ try:
+ from datetime import datetime
+ from pytz import timezone
+ def parse_utc_timestamp(date_str):
+ return round(datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone('UTC')).timestamp())
+
+ except ImportError:
+ print("Please install either python-dateutil or python-pytz")
+ exit # TODO raise exception
class Generic():
"""Object representing generic stream.
"""
self._connection = connection
if location: self._location = location
+ self.latest = None
self._stream = []
# since epoch
self.max_time = int(time.mktime(time.gmtime()))
"""
params = {}
if max_time:
+ if self.latest == None:
+ self.latest = int(time.mktime(time.gmtime()) * 1000)
+ self.latest -= max_time
+ else: self.latest += 1
params['max_time'] = max_time
- params['_'] = int(time.time() * 1000)
+ params['_'] = self.latest
+ print("Diaspy _obtain.params: {}".format(params))
request = self._connection.get(self._location, params=params)
if request.status_code != 200:
raise errors.StreamError('wrong status code: {0}'.format(request.status_code))
posts = []
+ latest_time = None # Used to get the created_at from the latest posts we received.
for post in request.json():
try:
- posts.append(Post(self._connection, id=post['id'], guid=post['guid']))
+ comments = False
+ if post['interactions']['comments_count'] > 3: comments = True
+ posts.append(Post(self._connection, id=post['id'], guid=post['guid'], fetch=False, comments=comments, post_data=post))
+ if post['created_at']: latest_time = post['created_at']
except errors.PostError:
if not suppress:
raise
+ if latest_time:
+ self.max_time = parse_utc_timestamp( latest_time )
return posts
def _expand(self, new_stream):
def more(self, max_time=0, backtime=86400):
"""Tries to download more (older posts) posts from Stream.
+ TODO backtime isn't used anymore.
+ Diaspora reference: https://github.com/diaspora/diaspora/blob/26a9e50ef935628c800f9a21d345057556fa5c31/app/helpers/stream_helper.rb#L48
+
:param backtime: how many seconds substract each time (defaults to one day)
:type backtime: int
:param max_time: seconds since epoch (optional, diaspy'll figure everything on its own)
:type max_time: int
"""
- if not max_time: max_time = self.max_time - backtime
+
+ if not max_time: max_time = self.max_time
self.max_time = max_time
new_stream = self._obtain(max_time=max_time)
self._expand(new_stream)
location = 'people/{}/stream.json'.format(guid)
super().__init__(connection, location, fetch)
- def _obtain(self, max_time=0):
- """Obtains stream from pod.
- """
- params = {}
- if max_time: params['max_time'] = max_time
- request = self._connection.get(self._location, params=params)
- if request.status_code != 200:
- raise errors.StreamError('wrong status code: {0}'.format(request.status_code))
- return [Post(self._connection, post['id']) for post in request.json()]
-
-
class Stream(Generic):
"""The main stream containing the combined posts of the
followed users and tags and the community spotlights posts
"""
location = 'stream.json'
- def post(self, text='', aspect_ids='public', photos=None, photo='', provider_display_name=''):
+ def post(self, text='', aspect_ids='public', photos=None, photo='', poll_question=None, poll_answers=None, location_coords=None, provider_display_name=''):
"""This function sends a post to an aspect.
If both `photo` and `photos` are specified `photos` takes precedence.
:param text: Text to post.
:type text: str
+
:param aspect_ids: Aspect ids to send post to.
:type aspect_ids: str
+
:param photo: filename of photo to post
:type photo: str
+
:param photos: id of photo to post (obtained from _photoupload())
:type photos: int
+
:param provider_display_name: name of provider displayed under the post
:type provider_display_name: str
+ :param poll_question: Question string
+ :type poll_question: str
+
+ :param poll_answers: Anwsers to the poll
+ :type poll_answers: list with strings
+
+ :param location_coords: TODO
+ :type location_coords: TODO
+
:returns: diaspy.models.Post -- the Post which has been created
"""
data = {}
data['status_message'] = {'text': text, 'provider_display_name': provider_display_name}
if photo: data['photos'] = self._photoupload(photo)
if photos: data['photos'] = photos
+ if poll_question and poll_answers:
+ data['poll_question'] = poll_question
+ data['poll_answers'] = poll_answers
+ if location_coords: data['location_coords'] = location_coords
request = self._connection.post('status_messages',
data=json.dumps(data),
'x-csrf-token': repr(self._connection)})
if request.status_code != 201:
raise Exception('{0}: Post could not be posted.'.format(request.status_code))
- data = request.json()
- post = Post(self._connection, data['guid'])
- post.data(data)
+ post_json = request.json()
+ post = Post(self._connection, id=post_json['id'], guid=post_json['guid'], post_data=post_json)
return post
def _photoupload(self, filename, aspects=[]):
:parameter ids: list of apsect ids
:type ids: list of integers
"""
- self._location = 'aspects.json' + '?{0}'.format(','.join(ids))
- self.fill()
+ self._location = 'aspects.json?a_ids[]=' + '{}'.format('&a_ids[]='.join(ids))
+ self.fill() # this will create entirely new list of posts.
def add(self, aspect_name, visible=0):
"""This function adds a new aspect.