Completed
Pull Request — master (#309)
by Steve
01:16
created

AbstractAction

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 107

6 Methods

Rating   Name   Duplication   Size   Complexity  
A timesince() 0 6 1
A action_object_url() 0 6 1
A __str__() 0 15 4
A target_url() 0 6 1
A get_absolute_url() 0 3 1
A actor_url() 0 6 1
1
from __future__ import unicode_literals
2
3
import django
4
from django.apps import apps as django_apps
5
from django.db import models
6
from django.core.urlresolvers import reverse
7
from django.utils.translation import ugettext as _
8
from django.utils.encoding import python_2_unicode_compatible
9
from django.utils.timesince import timesince as djtimesince
10
from django.contrib.contenttypes.models import ContentType
11
12
13
try:
14
    from django.utils import timezone
15
    now = timezone.now
16
except ImportError:
17
    from datetime import datetime
18
    now = datetime.now
19
20
from actstream import settings as actstream_settings
21
from actstream.managers import FollowManager
22
from actstream.compat import user_model_label, generic
23
24
25
@python_2_unicode_compatible
26
class AbstractFollow(models.Model):
27
    """
28
    Lets a user follow the activities of any specific actor
29
    """
30
    user = models.ForeignKey(user_model_label, db_index=True)
31
32
    content_type = models.ForeignKey(ContentType, db_index=True)
33
    object_id = models.CharField(max_length=255, db_index=True)
34
    follow_object = generic.GenericForeignKey()
35
    actor_only = models.BooleanField("Only follow actions where "
36
                                     "the object is the target.", default=True)
37
    started = models.DateTimeField(default=now, db_index=True)
38
    objects = FollowManager()
39
40
    class Meta:
41
        abstract = True
42
        unique_together = ('user', 'content_type', 'object_id')
43
44
    def __str__(self):
45
        return '%s -> %s' % (self.user, self.follow_object)
46
47
48
@python_2_unicode_compatible
49
class AbstractAction(models.Model):
50
    """
51
    Action model describing the actor acting out a verb (on an optional
52
    target).
53
    Nomenclature based on http://activitystrea.ms/specs/atom/1.0/
54
55
    Generalized Format::
56
57
        <actor> <verb> <time>
58
        <actor> <verb> <target> <time>
59
        <actor> <verb> <action_object> <target> <time>
60
61
    Examples::
62
63
        <justquick> <reached level 60> <1 minute ago>
64
        <brosner> <commented on> <pinax/pinax> <2 hours ago>
65
        <washingtontimes> <started follow> <justquick> <8 minutes ago>
66
        <mitsuhiko> <closed> <issue 70> on <mitsuhiko/flask> <about 2 hours ago>
67
68
    Unicode Representation::
69
70
        justquick reached level 60 1 minute ago
71
        mitsuhiko closed issue 70 on mitsuhiko/flask 3 hours ago
72
73
    HTML Representation::
74
75
        <a href="http://oebfare.com/">brosner</a> commented on <a href="http://github.com/pinax/pinax">pinax/pinax</a> 2 hours ago
76
77
    """
78
    actor_content_type = models.ForeignKey(ContentType, related_name='actor',
79
                                           db_index=True)
80
    actor_object_id = models.CharField(max_length=255, db_index=True)
81
    actor = generic.GenericForeignKey('actor_content_type', 'actor_object_id')
82
83
    verb = models.CharField(max_length=255, db_index=True)
84
    description = models.TextField(blank=True, null=True)
85
86
    target_content_type = models.ForeignKey(ContentType, blank=True, null=True,
87
                                            related_name='target', db_index=True)
88
    target_object_id = models.CharField(max_length=255, blank=True, null=True, db_index=True)
89
    target = generic.GenericForeignKey('target_content_type',
90
                                       'target_object_id')
91
92
    action_object_content_type = models.ForeignKey(ContentType, blank=True, null=True,
93
                                                   related_name='action_object', db_index=True)
94
    action_object_object_id = models.CharField(max_length=255, blank=True, null=True, db_index=True)
95
    action_object = generic.GenericForeignKey('action_object_content_type',
96
                                              'action_object_object_id')
97
98
    timestamp = models.DateTimeField(default=now, db_index=True)
99
100
    public = models.BooleanField(default=True, db_index=True)
101
102
    objects = actstream_settings.get_action_manager()
103
104
    class Meta:
105
        abstract = True
106
        ordering = ('-timestamp', )
107
108
    def __str__(self):
109
        ctx = {
110
            'actor': self.actor,
111
            'verb': self.verb,
112
            'action_object': self.action_object,
113
            'target': self.target,
114
            'timesince': self.timesince()
115
        }
116
        if self.target:
117
            if self.action_object:
118
                return _('%(actor)s %(verb)s %(action_object)s on %(target)s %(timesince)s ago') % ctx
119
            return _('%(actor)s %(verb)s %(target)s %(timesince)s ago') % ctx
120
        if self.action_object:
121
            return _('%(actor)s %(verb)s %(action_object)s %(timesince)s ago') % ctx
122
        return _('%(actor)s %(verb)s %(timesince)s ago') % ctx
123
124
    def actor_url(self):
125
        """
126
        Returns the URL to the ``actstream_actor`` view for the current actor.
127
        """
128
        return reverse('actstream_actor', None,
129
                       (self.actor_content_type.pk, self.actor_object_id))
130
131
    def target_url(self):
132
        """
133
        Returns the URL to the ``actstream_actor`` view for the current target.
134
        """
135
        return reverse('actstream_actor', None,
136
                       (self.target_content_type.pk, self.target_object_id))
137
138
    def action_object_url(self):
139
        """
140
        Returns the URL to the ``actstream_action_object`` view for the current action object
141
        """
142
        return reverse('actstream_actor', None, (
143
            self.action_object_content_type.pk, self.action_object_object_id))
144
145
    def timesince(self, now=None):
146
        """
147
        Shortcut for the ``django.utils.timesince.timesince`` function of the
148
        current timestamp.
149
        """
150
        return djtimesince(self.timestamp, now).encode('utf8').replace(b'\xc2\xa0', b' ').decode('utf8')
151
152
    @models.permalink
153
    def get_absolute_url(self):
154
        return 'actstream.views.detail', [self.pk]
155
156
157
class Action(AbstractAction):
158
    class Meta(AbstractAction.Meta):
159
        swappable = 'ACTSTREAM_ACTION_MODEL'
160
161
162
class Follow(AbstractFollow):
163
    class Meta(AbstractFollow.Meta):
164
        swappable = 'ACTSTREAM_FOLLOW_MODEL'
165
166
167
# convenient accessors
168
actor_stream = Action.objects.actor
169
action_object_stream = Action.objects.action_object
170
target_stream = Action.objects.target
171
user_stream = Action.objects.user
172
model_stream = Action.objects.model_actions
173
any_stream = Action.objects.any
174
followers = Follow.objects.followers
175
following = Follow.objects.following
176
177
178
if django.VERSION[:2] < (1, 7):
179
    from actstream.apps import ActstreamConfig
180
181
    ActstreamConfig().ready()
182