Completed
Pull Request — master (#390)
by
unknown
25s
created

AbstractAction.action_object_url()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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