Code Monkey home page Code Monkey logo

stream-django's Introduction

Stream Django

build PyPI version

stream-django is a Django client for Stream, it supports Django from 1.11 up to and including 4.0 using Python 2.7 and 3.4, 3.5, 3.6+.

You can sign up for a Stream account at https://getstream.io/get_started.

Note there is also a lower level Python - Stream integration library which is suitable for all Python applications.

๐Ÿ’ก This is a library for the Feeds product. The Chat SDKs can be found here.

Build activity streams & news feeds

Examples of what you can build

You can build:

  • Activity streams such as seen on Github
  • A twitter style newsfeed
  • A feed like instagram/ pinterest
  • Facebook style newsfeeds
  • A notification system

Example apps

You can check out our example apps built using this library (you can deploy them directly to Heroku with 1 click):

Table of Contents

Installation

Install stream_django package with pip:

pip install stream_django

add stream_django to your INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'stream_django'
]

STREAM_API_KEY = 'my_api_key'
STREAM_API_SECRET = 'my_api_secret_key'

Login with Github on getstream.io and add STREAM_API_KEY and STREAM_API_SECRET to your Django settings module (you can find them in the dashboard).

Model integration

Stream Django can automatically publish new activities to your feed. Simple mixin the Activity class on the models you want to publish.

from stream_django.activity import Activity

class Tweet(models.Model, Activity):
    ...

class Like(models.Model, Activity):
    ...

Every time a Tweet is created it will be added to the user's feed. Users which follow the given user will also automatically get the new tweet in their feeds.

Activity fields

Models are stored in feeds as activities. An activity is composed of at least the following fields: actor, verb, object, time. You can also add more custom data if needed. The Activity mixin will try to set things up automatically:

object is a reference to the model instance actor is a reference to the user attribute of the instance verb is a string representation of the class name

By default the actor field will look for an attribute called user or actor and a field called created_at to track creation time. If you're user field is called differently you'll need to tell us where to look for it. Below shows an example how to set things up if your user field is called author.

class Tweet(models.Model, Activity):
    created_at = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    @property
    def activity_actor_attr(self):
        return self.author

Activity extra data

Often you'll want to store more data than just the basic fields. You achieve this by implementing the extra_activity_data method in the model.

NOTE: you should only return data that json.dumps can handle (datetime instances are supported too).

class Tweet(models.Model, Activity):

    @property
    def extra_activity_data(self):
        return {'is_retweet': self.is_retweet }

Feed manager

Django Stream comes with a feed_manager class that helps with all common feed operations.

Feeds bundled with feed_manager

To get you started the manager has 4 feeds pre configured. You can add more feeds if your application needs it. The three feeds are divided in three categories.

User feed:

The user feed stores all activities for a user. Think of it as your personal Facebook page. You can easily get this feed from the manager.

from stream_django.feed_manager import feed_manager

feed_manager.get_user_feed(user_id)
News feeds:

The news feeds (or timelines) store the activities from the people you follow. There is both a simple timeline newsfeed (similar to twitter) and an aggregated version (like facebook).

timeline = feed_manager.get_news_feeds(user_id)['timeline']
timeline_aggregated = feed_manager.get_news_feeds(user_id)['timeline_aggregated']
Notification feed:

The notification feed can be used to build notification functionality.

Notification feed

Below we show an example of how you can read the notification feed.

notification_feed = feed_manager.get_notification_feed(user_id)

By default the notification feed will be empty. You can specify which users to notify when your model gets created. In the case of a retweet you probably want to notify the user of the parent tweet.

class Tweet(models.Model, Activity):

    @property
    def activity_notify(self):
        if self.is_retweet and self.parent_tweet.author != self.author:
            target_feed = feed_manager.get_notification_feed(self.parent_tweet.author_id)
            return [target_feed]

Another example would be following a user. You would commonly want to notify the user which is being followed.

class Follow(models.Model, Activity):

    @property
    def activity_notify(self):
        return [feed_manager.get_notification_feed(self.target_user.id)]

Follow a feed

To create the newsfeeds you need to notify the system about follow relationships. The manager comes with APIs to let a user's news feeds follow another user's feed. This code lets the current user's timeline and timeline_aggregated feeds follow the target_user's personal feed.

feed_manager.follow_user(request.user.id, target_user)

Showing the newsfeed

Activity enrichment

When you read data from feeds, a like activity will look like this:

{'actor': 'core.User:1', 'verb': 'like', 'object': 'core.Like:42'}

This is far from ready for usage in your template. We call the process of loading the references from the database enrichment. An example is shown below:

from stream_django.enrich import Enrich

enricher = Enrich()
feed = feed_manager.get_feed('timeline', request.user.id)
activities = feed.get(limit=25)['results']
enriched_activities = enricher.enrich_activities(activities)

Templating

Now that you've enriched the activities you can render the template. For convenience we include the render activity template tag:

{% load activity_tags %}

{% for activity in activities %}
    {% render_activity activity %}
{% endfor %}

The render_activity template tag will render the template activity/[aggregated]/[verb].html with the activity as context.

For example activity/tweet.html will be used to render an normal activity with verb tweet

{{ activity.actor.username }} said "{{ activity.object.body }} {{ activity.created_at|timesince }} ago"

and activity/aggregated/like.html for an aggregated activity with verb like

{{ activity.actor_count }} user{{ activity.actor_count|pluralize }} liked {% render_activity activity.activities.0 %}

If you need to support different kind of templates for the same activity, you can send a third parameter to change the template selection.

The example below will use the template activity/[aggregated]/homepage_%(verb)s.html

{% render_activity activity 'homepage' %}

Settings

STREAM_API_KEY Your stream site api key. Default ''

STREAM_API_SECRET Your stream site api key secret. Default ''

STREAM_LOCATION The location API endpoint the client will connect to. Eg: STREAM_LOCATION='us-east'

STREAM_TIMEOUT The connection timeout (in seconds) for the API client. Default 6.0

STREAM_FEED_MANAGER_CLASS The path to the feed manager class. Default 'stream_django.managers.FeedManager'

STREAM_USER_FEED The name of the feed (as it is configured in your GetStream.io Dasboard) where activities are stored. Default 'user'

STREAM_NEWS_FEEDS The name of the news feed (as they are configured in your GetStream.io Dasboard) where activities from followed feeds are stored. Default {'timeline':'timeline', 'timeline_aggregated':'timeline_aggregated'}

STREAM_NOTIFICATION_FEED The name of the feed (as it is configured in your GetStream.io Dasboard) where activity notifications are stored. Default 'notification'

STREAM_DISABLE_MODEL_TRACKING Disable automatic tracking of Activity models. Default False

Temporarily disabling the signals

Model synchronization is disabled during schema/data migrations runs, syncdb and fixture loading (and during django test runs). You can completely disable feed publishing via the STREAM_DISABLE_MODEL_TRACKING django setting.

Customizing enrichment

Sometimes you'll want to customize how enrichment works. The documentation will show you several common options.

Enrich extra fields

If you store references to model instances in the activity extra_data you can use the Enrich class to take care of it for you

from stream_django.activity import create_model_reference

class Tweet(models.Model, Activity):

    @property
    def extra_activity_data(self):
        ref = create_model_reference(self.parent_tweet)
        return {'parent_tweet': ref }


# instruct the enricher to enrich actor, object and parent_tweet fields
enricher = Enrich(fields=['actor', 'object', 'parent_tweet'])
feed = feed_manager.get_feed('timeline', request.user.id)
activities = feed.get(limit=25)['results']
enriched_activities = enricher.enrich_activities(activities)

Change how models are retrieved

The enrich class that comes with the packages tries to minimise the amount of database queries. The models are grouped by their class and then retrieved with a pk__in query. You can implement a different approach to retrieve the instances of a model subclassing the stream_django.enrich.Enrich class.

To change the retrieval for every model you should override the fetch_model_instances method; in alternative you can change how certain models' are retrieved by implementing the hook function fetch_<model_name>_instances

class MyEnrich(Enrich):
    '''
    Overwrites how model instances are fetched from the database
    '''

    def fetch_model_instances(self, modelClass, pks):
        '''
        returns a dict {id:modelInstance} with instances of model modelClass
        and pk in pks
        '''
        ...

class AnotherEnrich(Enrich):
    '''
    Overwrites how Likes instances are fetched from the database
    '''

    def fetch_like_instances(self, pks):
        return {l.id: l for l in Like.objects.cached_likes(ids)}

Preload related data

You will commonly access related objects such as activity['object'].user. To prevent your newsfeed to run N queries you can instruct the manager to load related objects. The manager will use Django's select_related functionality. (https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related).

class Tweet(models.Model, Activity):

    @classmethod
    def activity_related_models(cls):
        return ['user']

Full documentation and Low level APIs access

When needed you can also use the low level Python API directly. Documentation is available at the Stream website.

from stream_django.client import stream_client

special_feed = stream_client.feed('special:42')
special_feed.follow('timeline:60')

Copyright and License Information

Copyright (c) 2014-2022 Stream.io Inc, and individual contributors. All rights reserved.

See the file "LICENSE" for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES.

stream-django's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stream-django's Issues

better support for pagination

we can't support django.core.paginator.Paginator (we need pk based pagination). best way to go is to ship our own paginator and make sure that it plays nicely with django.views.generic.list ListView

Django 1.11 support

Template tag is not working right now, there are two PR (#58 and #61) that almost fixed this. This part of the library is also not covered by tests.

Must implement activity_object_attr

Not sure if this is just me but it seems that the NotImplementedError that gives "Must implement activity_object_attr" is always thrown.

The property is defined in "activity.py" but will always throw the error unless the property is defined in the desired model with return self. Not sure why this is done this way and return self isn't just written in the mixin.

I am sure the docs should be updated if what I have is correct or "activity.py" be updated

How to fetch notifications with is_seen = False only??

I am fetching notifications through feed manager :
feed_manager.get_notification_feed(user.id)
But this method returns all the notifications whether seen or unseen.Is there a way to fetch only unseen notifications??

Recovering from a loss of data integrity / support for transactions

I'm running into some problems keeping data integrity between getstream and our database with transactions enabled, although the bigger problem is recovering from any loss of integrity. Problem is that at any point in the transaction the model could fail to save/delete and rollback, but the activity still remains in getstream.

At the moment I've had to settle on a hacky solution which just filters out any activity that fails to be linked to an object during the enriching process, but this feels a little bit inadequate. Ideally there would be a management command that I could run during maintenance to remove any deleted models from getstream, something like haystack's rebuild/update index commands, but looking at the api I'm not sure if that's possible.

Do you have any general advice / is there a way to build in better support for transactions?

pip install is broken

I can't install with pip -- it looks like the problem is that the required version of httpsig is no longer available.

Pip error message:
Could not find any downloads that satisfy the requirement httpsig==1.1.0 (from stream-python>=2.1.3- >stream-django)

And here's the status of httpsig:
$ yolk -V httpsig
httpsig 1.1.2

Get total follower/following counts

Currently the methods for getting the followers/following objects (using the feed_manager and stream_client classes) only returns the paginated list of items and not a count, this isn't very useful if you need to find the number of followers/following.

Currently:

{'results': [...], 'duration':'some_ms'}

Low level Api : follow_many() got an unexpected keyword argument 'activity_copy_limit'

from stream_django.client import stream_client

follows = [
  {'source': 'group:1', 'target': 'user:1'}, 
  {'source': 'group:1', 'target': 'user:2'},
 ]

stream_client.follow_many(follows,activity_copy_limit=10)

Error
Traceback (most recent call last):
File "", line 1, in
TypeError: follow_many() got an unexpected keyword argument 'activity_copy_limit'

Support for celery or other asynchronous queues?

Hi, I'm just considering whether or not to use this app or to create something in-house. Is celery supported or is it possible to build in some support for it through some middleware? When creating models aren't page load-times increased because of the network io - or have you already built in some asynchronous processing?

FeedConfigException: The following feeds are not configured: 'user'

>>> user=User.objects.get(id=1)
>>> feed=feed_manager.get_user_feed(user.id) 
>>> activity=feed.get(limit=25)['results']

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/stream/feed.py", line 119, in get
    self.feed_url, params=params, signature=token)
  File "/usr/local/lib/python2.7/dist-packages/stream/client.py", line 192, in get
    return self._make_request(self.session.get, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/stream/client.py", line 153, in _make_request
    return self._parse_response(response)
  File "/usr/local/lib/python2.7/dist-packages/stream/client.py", line 110, in _parse_response
    self.raise_exception(parsed_result, status_code=response.status_code)
  File "/usr/local/lib/python2.7/dist-packages/stream/client.py", line 180, in raise_exception
    raise exception
FeedConfigException: The following feeds are not configured: 'user'

Stream would not install because of pypcrypto on Windows 10

I tried installing stream-django using pip and this is the output from the command prompt shell. I am on WIndows 10 and using Python 3.6.2.

copying lib\Crypto\Hash\MD4.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\MD5.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\RIPEMD.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\SHA.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\SHA224.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\SHA256.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\SHA384.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\SHA512.py -> build\lib.win32-3.6\Crypto\Hash
copying lib\Crypto\Hash\__init__.py -> build\lib.win32-3.6\Crypto\Hash
creating build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\AES.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\ARC2.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\ARC4.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\blockalgo.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\Blowfish.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\CAST.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\DES.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\DES3.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\PKCS1_OAEP.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\PKCS1_v1_5.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\XOR.py -> build\lib.win32-3.6\Crypto\Cipher
copying lib\Crypto\Cipher\__init__.py -> build\lib.win32-3.6\Crypto\Cipher
creating build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\asn1.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\Counter.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\number.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\py3compat.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\randpool.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\RFC1751.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\winrandom.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\_number_new.py -> build\lib.win32-3.6\Crypto\Util
copying lib\Crypto\Util\__init__.py -> build\lib.win32-3.6\Crypto\Util
creating build\lib.win32-3.6\Crypto\Random
copying lib\Crypto\Random\random.py -> build\lib.win32-3.6\Crypto\Random
copying lib\Crypto\Random\_UserFriendlyRNG.py -> build\lib.win32-3.6\Crypto\Random
copying lib\Crypto\Random\__init__.py -> build\lib.win32-3.6\Crypto\Random
creating build\lib.win32-3.6\Crypto\Random\Fortuna
copying lib\Crypto\Random\Fortuna\FortunaAccumulator.py -> build\lib.win32-3.6\Crypto\Random\Fortuna
copying lib\Crypto\Random\Fortuna\FortunaGenerator.py -> build\lib.win32-3.6\Crypto\Random\Fortuna
copying lib\Crypto\Random\Fortuna\SHAd256.py -> build\lib.win32-3.6\Crypto\Random\Fortuna
copying lib\Crypto\Random\Fortuna\__init__.py -> build\lib.win32-3.6\Crypto\Random\Fortuna
creating build\lib.win32-3.6\Crypto\Random\OSRNG
copying lib\Crypto\Random\OSRNG\fallback.py -> build\lib.win32-3.6\Crypto\Random\OSRNG
copying lib\Crypto\Random\OSRNG\nt.py -> build\lib.win32-3.6\Crypto\Random\OSRNG
copying lib\Crypto\Random\OSRNG\posix.py -> build\lib.win32-3.6\Crypto\Random\OSRNG
copying lib\Crypto\Random\OSRNG\rng_base.py -> build\lib.win32-3.6\Crypto\Random\OSRNG
copying lib\Crypto\Random\OSRNG\__init__.py -> build\lib.win32-3.6\Crypto\Random\OSRNG
creating build\lib.win32-3.6\Crypto\SelfTest
copying lib\Crypto\SelfTest\st_common.py -> build\lib.win32-3.6\Crypto\SelfTest
copying lib\Crypto\SelfTest\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest
creating build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\common.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_AES.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_ARC2.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_ARC4.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_Blowfish.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_CAST.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_DES.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_DES3.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_pkcs1_15.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_pkcs1_oaep.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\test_XOR.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
copying lib\Crypto\SelfTest\Cipher\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Cipher
creating build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\common.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_HMAC.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_MD2.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_MD4.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_MD5.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_RIPEMD.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_SHA.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_SHA224.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_SHA256.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_SHA384.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\test_SHA512.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
copying lib\Crypto\SelfTest\Hash\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Hash
creating build\lib.win32-3.6\Crypto\SelfTest\Protocol
copying lib\Crypto\SelfTest\Protocol\test_AllOrNothing.py -> build\lib.win32-3.6\Crypto\SelfTest\Protocol
copying lib\Crypto\SelfTest\Protocol\test_chaffing.py -> build\lib.win32-3.6\Crypto\SelfTest\Protocol
copying lib\Crypto\SelfTest\Protocol\test_KDF.py -> build\lib.win32-3.6\Crypto\SelfTest\Protocol
copying lib\Crypto\SelfTest\Protocol\test_rfc1751.py -> build\lib.win32-3.6\Crypto\SelfTest\Protocol
copying lib\Crypto\SelfTest\Protocol\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Protocol
creating build\lib.win32-3.6\Crypto\SelfTest\PublicKey
copying lib\Crypto\SelfTest\PublicKey\test_DSA.py -> build\lib.win32-3.6\Crypto\SelfTest\PublicKey
copying lib\Crypto\SelfTest\PublicKey\test_ElGamal.py -> build\lib.win32-3.6\Crypto\SelfTest\PublicKey
copying lib\Crypto\SelfTest\PublicKey\test_importKey.py -> build\lib.win32-3.6\Crypto\SelfTest\PublicKey
copying lib\Crypto\SelfTest\PublicKey\test_RSA.py -> build\lib.win32-3.6\Crypto\SelfTest\PublicKey
copying lib\Crypto\SelfTest\PublicKey\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\PublicKey
creating build\lib.win32-3.6\Crypto\SelfTest\Random
copying lib\Crypto\SelfTest\Random\test_random.py -> build\lib.win32-3.6\Crypto\SelfTest\Random
copying lib\Crypto\SelfTest\Random\test_rpoolcompat.py -> build\lib.win32-3.6\Crypto\SelfTest\Random
copying lib\Crypto\SelfTest\Random\test__UserFriendlyRNG.py -> build\lib.win32-3.6\Crypto\SelfTest\Random
copying lib\Crypto\SelfTest\Random\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Random
creating build\lib.win32-3.6\Crypto\SelfTest\Random\Fortuna
copying lib\Crypto\SelfTest\Random\Fortuna\test_FortunaAccumulator.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\Fortuna
copying lib\Crypto\SelfTest\Random\Fortuna\test_FortunaGenerator.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\Fortuna
copying lib\Crypto\SelfTest\Random\Fortuna\test_SHAd256.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\Fortuna
copying lib\Crypto\SelfTest\Random\Fortuna\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\Fortuna
creating build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
copying lib\Crypto\SelfTest\Random\OSRNG\test_fallback.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
copying lib\Crypto\SelfTest\Random\OSRNG\test_generic.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
copying lib\Crypto\SelfTest\Random\OSRNG\test_nt.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
copying lib\Crypto\SelfTest\Random\OSRNG\test_posix.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
copying lib\Crypto\SelfTest\Random\OSRNG\test_winrandom.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
copying lib\Crypto\SelfTest\Random\OSRNG\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Random\OSRNG
creating build\lib.win32-3.6\Crypto\SelfTest\Util
copying lib\Crypto\SelfTest\Util\test_asn1.py -> build\lib.win32-3.6\Crypto\SelfTest\Util
copying lib\Crypto\SelfTest\Util\test_Counter.py -> build\lib.win32-3.6\Crypto\SelfTest\Util
copying lib\Crypto\SelfTest\Util\test_number.py -> build\lib.win32-3.6\Crypto\SelfTest\Util
copying lib\Crypto\SelfTest\Util\test_winrandom.py -> build\lib.win32-3.6\Crypto\SelfTest\Util
copying lib\Crypto\SelfTest\Util\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Util
creating build\lib.win32-3.6\Crypto\SelfTest\Signature
copying lib\Crypto\SelfTest\Signature\test_pkcs1_15.py -> build\lib.win32-3.6\Crypto\SelfTest\Signature
copying lib\Crypto\SelfTest\Signature\test_pkcs1_pss.py -> build\lib.win32-3.6\Crypto\SelfTest\Signature
copying lib\Crypto\SelfTest\Signature\__init__.py -> build\lib.win32-3.6\Crypto\SelfTest\Signature
creating build\lib.win32-3.6\Crypto\Protocol
copying lib\Crypto\Protocol\AllOrNothing.py -> build\lib.win32-3.6\Crypto\Protocol
copying lib\Crypto\Protocol\Chaffing.py -> build\lib.win32-3.6\Crypto\Protocol
copying lib\Crypto\Protocol\KDF.py -> build\lib.win32-3.6\Crypto\Protocol
copying lib\Crypto\Protocol\__init__.py -> build\lib.win32-3.6\Crypto\Protocol
creating build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\DSA.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\ElGamal.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\pubkey.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\RSA.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\_DSA.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\_RSA.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\_slowmath.py -> build\lib.win32-3.6\Crypto\PublicKey
copying lib\Crypto\PublicKey\__init__.py -> build\lib.win32-3.6\Crypto\PublicKey
creating build\lib.win32-3.6\Crypto\Signature
copying lib\Crypto\Signature\PKCS1_PSS.py -> build\lib.win32-3.6\Crypto\Signature
copying lib\Crypto\Signature\PKCS1_v1_5.py -> build\lib.win32-3.6\Crypto\Signature
copying lib\Crypto\Signature\__init__.py -> build\lib.win32-3.6\Crypto\Signature
Skipping optional fixer: buffer
Skipping optional fixer: idioms
Skipping optional fixer: set_literal
Skipping optional fixer: ws_comma
running build_ext
warning: GMP or MPIR library not found; Not building Crypto.PublicKey._fastmath.
building 'Crypto.Random.OSRNG.winrandom' extension
creating build\temp.win32-3.6
creating build\temp.win32-3.6\Release
creating build\temp.win32-3.6\Release\src
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -Isrc/ -Isrc/inc-msvc/ -Ic:\users\kevin\appdata\local\programs\python\python36-32\include -Ic:\users\kevin\appdata\local\programs\python\python36-32\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\include\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\winrt" /Tcsrc/winrand.c /Fobuild\temp.win32-3.6\Release\src/winrand.obj
winrand.c
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(26): error C2061: syntax error: identifier 'intmax_t'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(27): error C2061: syntax error: identifier 'rem'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(27): error C2059: syntax error: ';'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(28): error C2059: syntax error: '}'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(30): error C2061: syntax error: identifier 'imaxdiv_t'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(30): error C2059: syntax error: ';'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(40): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(41): error C2146: syntax error: missing ')' before identifier '_Number'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(41): error C2061: syntax error: identifier '_Number'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(41): error C2059: syntax error: ';'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(42): error C2059: syntax error: ')'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(45): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(46): error C2146: syntax error: missing ')' before identifier '_Numerator'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(46): error C2061: syntax error: identifier '_Numerator'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(46): error C2059: syntax error: ';'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(46): error C2059: syntax error: ','
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(48): error C2059: syntax error: ')'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(50): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(56): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(63): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(69): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(76): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(82): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(89): error C2143: syntax error: missing '{' before '__cdecl'
C:\Program Files (x86)\Windows Kits\10\include\10.0.15063.0\ucrt\inttypes.h(95): error C2143: syntax error: missing '{' before '__cdecl'
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\cl.exe' failed with exit status 2

----------------------------------------

Command "c:\users\kevin\appdata\local\programs\python\python36-32\python.exe -u -c "import setuptools, tokenize;file='C:\Users\Kevin\AppData\Local\Temp\pip-build-xcbogoi3\pycrypto\setup.py';f=getattr(tokenize, 'open', open)(file);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, file, 'exec'))" install --record C:\Users\Kevin\AppData\Local\Temp\pip-krn43go5-record\install-record.txt --single-version-externally-managed --compile" failed with error code 1 in C:\Users\Kevin\AppData\Local\Temp\pip-build-xcbogoi3\pycrypto\

graceful enrichment

in the template tag; log a warning when an activity is not fully enriched and then skip the render step (so you don't end up breaking the page)

follow_user and unfollow_user specify a different feed (not the user feed)

Hello,

Would it be possible to add feed_type=None in:
https://github.com/GetStream/stream-django/blob/master/stream_django/managers.py#L39
and
https://github.com/GetStream/stream-django/blob/master/stream_django/managers.py#L45

E.g.

def follow_user(self, user_id, target_user_id, feed_type=None):
        news_feeds = self.get_news_feeds(user_id)
        target_feed = self.get_user_feed(target_user_id, feed_type)
        for feed in news_feeds.values():
            feed.follow(target_feed.slug, target_feed.user_id)

Doing so users can follow any feed. Anyway get_user_feed already has the parameter.

Usage:

@receiver(post_save, sender=UserFollowMyModel1)
@receiver(post_save, sender=UserFollowMyModel2)
def follow_feed(sender, instance, created, **kwargs):
    if created and not settings.STREAM_DISABLE_MODEL_TRACKING:
        target_unique_id = '{0}-{1}'.format(instance.target.type_id, instance.target_id)
        feed_manager.follow_user(instance.user_id, target_unique_id, 'my_custom_flat_feed')

Thanks,

Davide

'flat' feeds are not configured

Right now I have the functional feeds for each user's own tweet, but when I try to follow another user, it gave me this error: The following feeds are not configured: flat.
I tried to create the Follow object in the shell by follow = Follow.objects.create(user=one, target=two), and then the above error was thrown.
Any idea what went wrong? Thanks in advance.

Issue following users

Hi. I just joined stream and looks amazing, but I came across what I believe might be an issue with the django library. When I try following/unfollowing users the library fails to do so and spits out an exception.

This is the exception I'm getting:

InputException: Invalid value.
Field "target" errors: [u'Invalid value.']

This happens when using:
feed_manager.follow_user(user, instance.pk)
being user an int and instance a user object (instance.pk is also an int)

This is the request being generated:

POST /api/v1.0/feed/flat/1/follows/?api_key=xxxx HTTP/1.1
Content-Type: application/json
User-Agent: stream-javascript-client-2.0.0
Authorization: flat1 xxxxxxxxxxxxxxxxxx
Host: getstream.io
Connection: close
Content-Length: 67

{"target_token": "xxxxxxxxx", "target": "user:user:3"}

This is the response I'm getting:

HTTP/1.1 400 BAD REQUEST
Server: nginx/1.4.6 (Ubuntu)
Date: Fri, 14 Nov 2014 17:42:23 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: close
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
Allow: GET, POST, HEAD, OPTIONS

{"code": 4, "detail": "Errors for fields 'target'", "duration": "10ms", "exception": "InputException", "exception_fields": {"target": ["Invalid value."]}, "status_code": 400}

I found a workaround by editing stream_django.managers.py, replacing
feed.follow_user(target_feed.slug, target_feed.id)
with
feed.follow_user(target_feed.slug, target_user_id)

Same for feed.unfollow_user.

This changes the target field in the request body from "target": "user:user:3" to "target": "user:3", which looks more like the docs. This seems to be working properly, but I do not know the library well enough to consider this a fix or not.

BTW, I also tried running the tests you have in place for stream-django and apparently they are working properly, even when sending "target": "user:user:2". The http response code in that case is 200, so I don't know what to make out of all of this.

Probably it's just me doing something wrong, but I'm stuck right now. This is the furthest I could get. I would appreciate if someone could shed some light on this issue. Thanks.

RemovedInDjango19Warning: django.db.models.loading deprecated

Basically, I'm getting:

/Users/JJZ/.virtualenvs/athletesunited/lib/python3.4/importlib/_bootstrap.py:321: RemovedInDjango19Warning: The utilities in django.db.models.loading are deprecated in favor of the new application loading system.
return f(_args, *_kwds)

And I see in enrich.py:

from django.db.models.loading import get_model

Django now has apps:

https://docs.djangoproject.com/en/1.8/ref/applications/

so

try:
from django.apps import apps
get_model = apps.get_model
except ImportError:
from django.db.models.loading import get_model

should do it.

Thanks,

JJ

A method to add activity using the feed manager

To create my activities, I've been using the raw client.stream_client to add activities. But for them to be ready for enrichment, I'm afraid they activity data should have a specific format. Is there a method on the feed manager to do it?. I can't see it anywhere.

Frequent read timeouts in development server?

My colleague and I are being thrown the following error frequently in the development server:

Exception Type: ReadTimeout at /discussions/
Exception Value: HTTPSConnectionPool(host='eu-central-api.getstream.io', port=443): Read timed out. (read timeout=3.0)

Is there anything that could be causing this error aside from latency? Our concern at the moment is that when we move this to a production environment at the end of next month we'll still be faced with the same problem.

Here is the full traceback:

Environment:


Request Method: GET
Request URL: http://localhost:8000/discussions/

Django Version: 1.8.3
Python Version: 3.4.3
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.sites',
 'mptt',
 'search',
 'haystack',
 'social',
 'allauth',
 'allauth.account',
 'allauth.socialaccount',
 'rest_framework',
 'restapi',
 'core',
 'conversations',
 'sharestyle_auth',
 'discussions',
 'feed',
 'tags',
 'djcelery',
 'djcelery_email',
 'arrangements',
 'imagekit',
 'colorful',
 'celery_haystack',
 'stream_django',
 'displayable_models',
 'notifications')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware')


Traceback:
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
  164.                 response = response.render()
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/django/template/response.py" in render
  158.             self.content = self.rendered_content
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/django/template/response.py" in rendered_content
  135.         content = template.render(context, self._request)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/django/template/backends/django.py" in render
  74.         return self.template.render(context)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/django/template/base.py" in render
  208.                 with context.bind_template(self):
File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/contextlib.py" in __enter__
  59.             return next(self.gen)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/django/template/context.py" in bind_template
  241.             updates.update(processor(self.request))
File "/Users/sampeka/dev/thestylelocker/core/context_processors.py" in notifications
  25.   items = feed.get(limit=5, mark_seen=False)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/stream/feed.py" in get
  119.             self.feed_url, params=params, signature=token)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/stream/client.py" in get
  192.         return self._make_request(self.session.get, *args, **kwargs)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/stream/client.py" in _make_request
  150.                           params=default_params, timeout=self.timeout)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/requests/sessions.py" in get
  477.         return self.request('GET', url, **kwargs)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/requests/sessions.py" in request
  465.         resp = self.send(prep, **send_kwargs)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/requests/sessions.py" in send
  573.         r = adapter.send(request, **kwargs)
File "/Users/sampeka/virtualenv/thestylelocker/lib/python3.4/site-packages/requests/adapters.py" in send
  433.                 raise ReadTimeout(e, request=request)

Exception Type: ReadTimeout at /discussions/
Exception Value: HTTPSConnectionPool(host='eu-central-api.getstream.io', port=443): Read timed out. (read timeout=3.0)

Allow activity verbs with same name

I'm trying to display an activity feed for a group which could include a number of different activities which could be described as "created". For instance, the creation of the group itself, the creation of an invite to the group etc.

So I'd like to be able to create a template in the groups app called created.html but also a template in the invitations app called created.html.

The existing template tag will use the first one it finds and not use the app-specific one.

I was hoping that the tag might be able to use the target of the activity to try and figure this out but I'm not sure how to implement it so I can do a pull request.

Is this possible or am I barking up the wrong tree here? Maybe naming the templates different things is the only way?

add django restframework serializer for activities

right now serializing enriched data is very complex and requires lot of knowledge of how DRF works (and probably some smart tricks to support nested data / dynamic model serialization).

We should write a basic serializer class to make this easier and write a few example for aggregated/notification and simple activity serialization.

eg.

"is_seen": false,
"is_read": false,
"group": "19931_2016-04-04",
"created_at": "2016-04-04T08:53:42.601",
"updated_at": "2016-04-04T11:33:26.140",
"id": "0bc8c85a-fa59-11e5-8080-800005683205",
"verb": "message",
"activities": [
    {
    "origin": null,
    "verb": "message",
    "time": "2016-04-04T11:33:26.140",
    "id": "0bc8c85a-fa59-11e5-8080-800005683205",
    "foreign_id": "chat.Message:6",
    "target": null,
    "to": [
    "notification:1"
    ],
"actor": "auth.User:1",
"object": "chat.Message:6"
}

Unable to install stream because of pycrypto on ubuntu 14.04

I'm trying to install stream on my ubuntu 14.04 system using pip install stream_django on django framework. but it returns me an error on pycrypto package dependency. It there any alternative I can use to install stream_django package.?

Here are details of my development system.
-Computer-

Processor : 2x Intel(R) Core(TM)2 Duo CPU T6400 @ 2.00GHz
Memory : 3040MB (2455MB used)
Operating System : Ubuntu 14.04.5 LTS

-Operating System-

-Version-
Kernel : Linux 4.4.0-97-generic (x86_64)
Compiled : #120~14.04.1-Ubuntu SMP Wed Sep 20 15:53:13 UTC 2017
C Library : Unknown
Default C Compiler : Unknown
Distribution : Ubuntu 14.04.5 LTS

-Development Environment Setup-

Python 3.4.3 (default, Nov 17 2016, 01:08:31)
[GCC 4.8.4] on linux
Django==1.11.4
djangorestframework==3.6.3

Here is my pip install output file.

stream_pip_output.txt

UUID support for model IDs

The project I'm working with has model IDs in UUIDFields. Is there support for UUID IDs or is this designed for positive int IDs only?

Creating, retrieving, destroying notifications worked after some tweaks.
I had to make a small adjustment for UUIDs, using cleaned_id = str(my_obj.id.replace('-', '')) because if I kept the hyphens it would tell me I had to use an ID with only letters, numbers, and underscore (or something like that).

Enriching had some problems as well, when getting by UUID:
enriched_activities = enricher.enrich_aggregated_activities(activities)
File "/lib/python3.4/site-packages/stream_django/enrich.py", line 65, in enrich_aggregated_activities
self._inject_objects(activity['activities'], objects, self.fields)
File "/lib/python3.4/site-packages/stream_django/enrich.py", line 117, in _inject_objects
instance = objects[f_ct].get(int(f_id))
ValueError: invalid literal for int() with base 10: '0dfed761-d5e0-4a56-be85-d11f05842417'

I dropped int() from last line in enrich.py to instance = objects[f_ct].get(f_id) and I'm on my way.
It all seems to work now, but would these be breaking changes in something I haven't seen yet?

Thanks!
edit: 2+ months later, no issues with the above changes. ๐Ÿ‘

client.following is always empty

My models.py and client code is shown below

I have 2 users. In my django admin i created 2 follows, one that is {target: 1, user: 2} and {target:2, user:1}. Following always returns an empty array, when calling followers always has the expected result.

Why is following empty?

The client code below logs the following to console:

{
    "user1": {
        "following": {
            "duration": "22ms",
            "results": []
        }
    }
}
{
    "user1": {
        "followers": {
            "duration": "22ms",
            "results": [{
                "created_at": "2016-04-10T17:29:16.493Z",
                "feed_id": "aggregated:2",
                "target_id": "user:1",
                "updated_at": "2016-04-10T17:29:16.493Z"
            }, {
                "created_at": "2016-04-10T17:29:16.416Z",
                "feed_id": "flat:2",
                "target_id": "user:1",
                "updated_at": "2016-04-10T17:29:16.416Z"
            }]
        }
    }
} 
{
    "user2": {
        "followers": {
            "duration": "14ms",
            "results": [{
                "created_at": "2016-04-10T17:32:36.468Z",
                "feed_id": "aggregated:1",
                "target_id": "user:2",
                "updated_at": "2016-04-10T17:32:36.468Z"
            }, {
                "created_at": "2016-04-10T17:32:36.364Z",
                "feed_id": "flat:1",
                "target_id": "user:2",
                "updated_at": "2016-04-10T17:32:36.364Z"
            }]
        }
    }
} 
{
    "user2": {
        "following": {
            "duration": "14ms",
            "results": []
        }
    }
}

Here is my models.py

from django.conf import settings
from django.db import models

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.fields import GenericForeignKey

from django.db.models.signals import post_delete, post_save
from stream_django.activity import Activity
from stream_django.feed_manager import feed_manager

class BaseModel(models.Model):
    created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
    updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)

    class Meta:
        abstract = True

class Follow(Activity, BaseModel):
    '''
    A simple table mapping who a user is following.
    For example, if user is Kyle and Kyle is following Alex,
    the target would be Alex.
    '''
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, related_name='following_set')
    target = models.ForeignKey(
        settings.AUTH_USER_MODEL, related_name='follower_set')

    @classmethod
    def activity_related_models(cls):
        return ['user', 'target']

    @property
    def activity_object_attr(self):
        return self

    @property
    def activity_notify(self):
        target_feed = feed_manager.get_notification_feed(self.target_id)
        return [target_feed]

def follow_change(sender, instance, **kwargs):
    print(instance.target_id)
    print(instance.user_id)
    feed_manager.follow_user(instance.user_id, instance.target_id)

def unfollow_feed(sender, instance, **kwargs):
    feed_manager.unfollow_user(instance.user_id, instance.target_id)

post_save.connect(follow_change, sender=Follow)
post_delete.connect(unfollow_feed, sender=Follow)

and my client code looks like this:

import stream from 'getstream'
var client = stream.connect('11111111', null, '11111')
var token1 =  'usertoken1'
var token2 =  'usertoken2'

var user1 = client.feed('user', '1', token1)
var user2 = client.feed('user', '2', token2)

user1.following().then(function (data) {
  console.log(JSON.stringify({user1: { following: data }})) // always empty
})

user2.following().then(function (data) {
  console.log(JSON.stringify({user2: { following: data }})) // always empty
})

user1.followers().then(function (data) {
  console.log(JSON.stringify({user1: { followers: data }}))
})

user2.followers().then(function (data) {
  console.log(JSON.stringify({user2: { followers: data }}))
})

raise_exception() not returning exception details

I received the following traceback when trying to add an activity, with an empty exception message: http://dpaste.com/0J16QNB

After digging into it deeper, I discovered that result contained the following:

{u'exception': u'InputException', u'code': 4, u'status_code': 400, u'detail': u"Errors for fields 'verb'", u'duration': u'10ms', u'exception_fields': {u'verb': [u'Ensure this value has at most 20 characters (it has 26).', u'Field "verb" errors: [u\'Ensure this value has at most 20 characters (it has 26).\']']}}

As you can see - it was a simple issue - my verb was too long.
However, raise_exception was unable to return that information to me.

errors = errors_from_fields(exception_fields) does not property parse exception_fields, which is:
{u'verb': [u'Ensure this value has at most 20 characters (it has 26).', u'Field "verb" errors: [u\'Ensure this value has at most 20 characters (it has 26).\']']}

The function returns [], which is not correct.

Update model activity extra_activity_data

Hi all,
Is there any way to update a model activity's extra_activity_data?
For example, I have a blog post model with the int field 'views' that I include in the extra_activity_data property. How can I update on Stream that variable every time it changes?
I know there's the command in the low level Python API, but I wasn't able to get it work with django.
Thanks in advance!

Notify callback for all subscribers to a feed

I would like a callback function for every subscriber to a feed when a new item is added to that feed.

Using the Twitter clone as an example:

I would like to call a function every time an object is added to a feed. For example, I would like an email sent to the users. This function would have access to the object as well as the list of subscribers to the feed. Therefore every follower to a tweet would be emailed when a tweet is created, and when someone follows another user, that user would be emailed.

This could work in one of two ways: the function is called once for each subscriber, or the function is called once and has a list of subscribers.

Currently I doing this with a post_save signal on the Tweet and Follow models. I call get_followers on the tweets author, and send them each an email. I implement a similar method for the Follow model.

My question is, is there a better way to do this leveraging the stream framework? For my use case this does't scale well, as I have dozens of Models, and implementing post_save for all of them is very repetitive.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.