Commits

rmoch committed 1036237

add linked objects tracking system

  • Participants
  • Parent commits 8ee5293

Comments (0)

Files changed (6)

File notification/admin.py

 from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext_lazy as _
 
-from .models import SimpleRegistration, Notification, RecipientGroup #, EmailLogEntry
+from .models import SimpleRegistration, Notification, RecipientGroup, Hit
 
 
 def registered(obj):
 registered.allow_tags = True
 registered.short_description = _("Registred")
 
+
 class SimpleRegistrationAdmin(admin.ModelAdmin):
     list_display = ['registration_date', 'email', registered]
 admin.site.register(SimpleRegistration, SimpleRegistrationAdmin)
 
 admin.site.register(RecipientGroup)
 
+#admin.site.register(Hit)
+
 # TODO: We'd like to use forms.NotificationForm directly in admin, but dynamic fields won't show up.
 # http://stackoverflow.com/questions/5718838/django-adding-new-form-fields-dynamically-in-admin
 # This probleme is unsatisfyingly solved by overriding host project admin routes to notification model (add and edit)
     return '<a href="%s">Preview</a>' % reverse('view_notification', args=[obj.md5])
 preview_link.allow_tags = True
 
+
 class NotificationAdmin(admin.ModelAdmin):
     list_display = ['subject', 'body', 'creation_date', 'from_name', 'from_email', preview_link]
     ordering = ['-creation_date']

File notification/models.py

 class GetAttrMixin(object):
     """Retrieve a Class from a path of this form: 'project.application.models.Model'."""
     def _get_class(self, path):
-        return getattr(__import__(path.rsplit('.',1)[0], fromlist=['']), path.rsplit('.', 1)[1])
+        return getattr(__import__(path.rsplit('.', 1)[0], fromlist=['']), path.rsplit('.', 1)[1])
 
 
 class ModelWithMD5(models.Model):
         if not self.id:
             self.md5 = hash_md5(datetime.datetime.today().strftime('%f')).hexdigest()
         super(ModelWithMD5, self).save(*args, **kwargs)
+
     class Meta:
         abstract = True
 
             if isinstance(self.content_object, self._get_class(descr['class'])):
                 return descr
 
+    def get_instance(self):
+        Klass = self.content_type.model_class()
+        return Klass.objects.get(id=self.object_id)
+
     def __unicode__(self):
         descr_dict = self._get_descr()
-        attr = getattr(self.content_object, descr_dict['title_attr'])
-        title = attr() if callable(attr) else attr
-        return "%s: %s" % (descr_dict['description'], title)
+        if descr_dict:
+            attr = getattr(self.content_object, descr_dict['title_attr'])
+            title = attr() if callable(attr) else attr
+            return "%s: %s" % (descr_dict['description'], title)
+        else:
+            # well it's ambarrassing, it seems we sometimes have wrong contentypes in bdd
+            return '%d %s' % (self.object_id, self.content_type)
 
 
 class RecipientGroup(models.Model):
         context['notification'] = self
         context['object_types'] = {}
 
-        # optimisation possible here
+        # possible optimisation here
         for item in self.items.all():
             Klass = item.content_type.model_class()
             for object_type in settings.NOTIFICATION_ASSOCIATED_OBJECTS:
                         context['object_types'][object_type['name']] = {}
                         context['object_types'][object_type['name']]['long_description'] = object_type['long_description']
                         context['object_types'][object_type['name']]['instances'] = []
-                    context['object_types'][object_type['name']]['instances'].append(Klass.objects.get(id=item.object_id))
+                    instance = Klass.objects.get(id=item.object_id)
+                    instance.item = item
+                    context['object_types'][object_type['name']]['instances'].append(instance)
         return context
 
     def get_sender(self):
         self.sent_date = datetime.datetime.now()
         self.save()
 
+
+class Hit(models.Model):
+    """Record for statistics links clicked by recipients."""
+    notification = models.ForeignKey(Notification, verbose_name=_(u"Notification"))
+    item = models.ForeignKey(Item, verbose_name=_(u"Item"))
+    recipient = models.ForeignKey(SimpleRegistration, verbose_name=_(u"Recipient"))
+    hit_date = models.DateTimeField(default=datetime.datetime.now, verbose_name=_(u"Hit date"))
+    count = models.PositiveIntegerField(default=1, verbose_name=_(u'Hits count'))
+
+    class Metal:
+        verbose_name = _(u"Hit")
+        verbose_name_plural = _(u"Hits")
+
+    def __unicode__(self):
+        return u"%s-%s-%s %s" % (self.notification, self.item, self.recipient, self.hit_date)
+
+
 # not used yet
 class EmailLogEntry(models.Model):
     """Model to queue email to send and log sent one."""
             self._subject = value
 
     subject = property(get_subject, set_subject)
-

File notification/templates/notification/emails/tracked_link.html

+{% load url from future %}
+{% if instance.item and recipient.md5 %}
+    <a href="http://{{ site }}{% url 'track_link' notification.md5 instance.item.pk recipient.md5 %}">{{ instance.get_title }}</a>
+{% else %}
+    {% comment %}
+        If we preview the notification or send it to a unregistred recipient, we can't track links
+    {% endcomment %}
+    {% include 'notification/emails/untracked_link.html' %}
+{% endif %}

File notification/templates/notification/emails/untracked_link.html

+<a href="http://{{ site }}{{ instance.get_absolute_url }}" style="color: #666;">{{ instance.get_title }}</a>

File notification/urls.py

 
     url(r'^view/(?P<md5>[\d\w]+)/$', 'view_notification', name='view_notification'),
 
+    url(r'^track/(?P<notification_md5>[\d\w]+)/(?P<item_pk>[\d\w]+)/(?P<recipient_md5>[\d\w]+)/$', 'track_link', name='track_link')
 )

File notification/views.py

 from django.conf import settings
 from django.contrib.auth.decorators import login_required
 from django.contrib import messages
+from django.core.exceptions import ObjectDoesNotExist
 from django.http import Http404
 #from django.core.urlresolvers import reverse
 from django.shortcuts import redirect, render, get_object_or_404
 from django.views.decorators.http import require_POST
 
 from .forms import SimpleRegistrationForm, NotificationForm
-from .models import Notification, SimpleRegistration
+from .models import Notification, SimpleRegistration, Item, Hit
 
 
 @require_POST
     return render(request, "notification/notification_list.html", {
             'notifications': notifications,
     })
+
+
+def track_link(request, notification_md5, item_pk, recipient_md5):
+    try:
+        notification = Notification.objects.get(md5=notification_md5)
+        item = Item.objects.get(pk=item_pk)
+        recipient = SimpleRegistration.objects.get(md5=recipient_md5)
+    except ObjectDoesNotExist:
+        return redirect('/')
+    hit, created = Hit.objects.get_or_create(notification=notification,
+            item=item,
+            recipient=recipient)
+    if not created:
+        hit.count += 1
+        hit.save()
+    return redirect(item.get_instance().get_absolute_url())