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

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