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