Completed
Pull Request — master (#387)
by
unknown
42s
created

Action.get_absolute_url()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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