From 2dfcfc3f8e44d953bb83b8d0ff0a37b83c99a746 Mon Sep 17 00:00:00 2001
From: Arnaud Fabre <arnaud.fabre@camobscura.fr>
Date: Mon, 29 Jun 2015 14:12:46 +0200
Subject: [PATCH] adds TimeStampedModel and HashableModel

---
 representatives_votes/admin.py                | 11 ++--
 .../commands/import_dossier_from_toutatis.py  | 28 ++++------
 representatives_votes/models.py               | 24 +++++---
 representatives_votes/serializers.py          |  9 +--
 representatives_votes/{utils.py => tasks.py}  | 56 ++++++++++++++++++-
 5 files changed, 87 insertions(+), 41 deletions(-)
 rename representatives_votes/{utils.py => tasks.py} (50%)

diff --git a/representatives_votes/admin.py b/representatives_votes/admin.py
index c9c2d09..0add978 100644
--- a/representatives_votes/admin.py
+++ b/representatives_votes/admin.py
@@ -5,13 +5,13 @@ from .models import Dossier, Proposal, Vote
 
 
 class DossierAdmin(admin.ModelAdmin):
-    list_display = ('reference', 'title', 'link')
-    search_fields = ('reference', 'title')
+    list_display = ('id', 'fingerprint', 'reference', 'title', 'link')
+    search_fields = ('reference', 'title', 'fingerprint')
 
 
 class ProposalAdmin(admin.ModelAdmin):
-    list_display = ('reference', 'dossier_reference', 'title', 'datetime', 'kind', 'total_abstain', 'total_against', 'total_for')
-    search_fields = ('reference', 'dossier__reference', 'title',)
+    list_display = ('id', 'fingerprint', 'reference', 'dossier_reference', 'title', 'datetime', 'kind', 'total_abstain', 'total_against', 'total_for')
+    search_fields = ('reference', 'dossier__reference', 'title', 'fingerprint')
     def dossier_reference(self, obj):
         return obj.dossier.reference
 
@@ -29,6 +29,7 @@ class NoneMatchingFilter(admin.SimpleListFilter):
         else:
             return queryset            
 
+
 class VoteAdmin(admin.ModelAdmin):
     list_display = ('id', 'proposal_reference', 'position', 'representative_name', 'representative_remote_id')
     list_filter = (NoneMatchingFilter,)
@@ -37,7 +38,5 @@ class VoteAdmin(admin.ModelAdmin):
         return obj.proposal.reference
 
 admin.site.register(Dossier, DossierAdmin)
-
 admin.site.register(Proposal, ProposalAdmin)
-
 admin.site.register(Vote, VoteAdmin)
diff --git a/representatives_votes/management/commands/import_dossier_from_toutatis.py b/representatives_votes/management/commands/import_dossier_from_toutatis.py
index 7978754..dcf3e0a 100644
--- a/representatives_votes/management/commands/import_dossier_from_toutatis.py
+++ b/representatives_votes/management/commands/import_dossier_from_toutatis.py
@@ -18,25 +18,21 @@
 #
 # Copyright (C) 2013 Laurent Peuch <cortex@worlddomination.be>
 # Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
-import json
-from urllib2 import urlopen
 
 from django.core.management.base import BaseCommand
-from django.conf import settings
-
-from representatives_votes.utils import import_a_dossier
+from representatives_votes.tasks import import_a_dossier_from_toutatis
 
 class Command(BaseCommand):
+    """
+    Command to import a dossier from a toutatis server
+    """
+    
+    def add_arguments(self, parser):
+        parser.add_argument('--celery', action='store_true', default=False)
+        
     def handle(self, *args, **options):
         reference = args[0]
-        toutatis_server = getattr(settings,
-                                  'TOUTATIS_SERVER',
-                                  'http://toutatis.mm.staz.be')
-        search_url = toutatis_server + '/api/dossiers/?reference=%s' % reference
-        print('Import dossier from %s' % search_url)
-        data = json.load(urlopen(search_url))
-        if data['count'] != 1:
-            raise Exception('Search should return one and only one result')
-        detail_url = data['results'][0]['url']
-        data = json.load(urlopen(detail_url))
-        import_a_dossier(data)
+        if options['celery']:
+            import_a_dossier_from_toutatis.delay(reference, delay=True)
+        else:
+            import_a_dossier_from_toutatis(reference, delay=False)
diff --git a/representatives_votes/models.py b/representatives_votes/models.py
index 0768ef2..f3b64b2 100644
--- a/representatives_votes/models.py
+++ b/representatives_votes/models.py
@@ -16,17 +16,27 @@
 # License along with django-representatives.
 # If not, see <http://www.gnu.org/licenses/>.
 
+import hashlib
+
 from django.db import models
+from django.utils.functional import cached_property
+from django.utils.encoding import smart_str
 
+from representatives.models import TimeStampedModel, HashableModel
 
-class Dossier(models.Model):
+class Dossier(HashableModel, TimeStampedModel):
     title = models.CharField(max_length=1000)
     reference = models.CharField(max_length=200)
     text = models.TextField(blank=True, default='')
     link = models.URLField()
 
+    hashable_fields = ['title', 'reference']
+    
+    def __unicode__(self):
+        return unicode(self.title)
+
 
-class Proposal(models.Model):
+class Proposal(HashableModel, TimeStampedModel):
     dossier = models.ForeignKey(Dossier, related_name='proposals')
     title = models.CharField(max_length=1000)
     description = models.TextField(blank=True, default='')
@@ -37,13 +47,9 @@ class Proposal(models.Model):
     total_against = models.IntegerField()
     total_for = models.IntegerField()
 
-    # Presentation for the api
-    def vote_api_list(self):
-        return [{'position': vote.position,
-                 'representative_remote_id': vote.representative_remote_id,
-                 'representative_name': vote.representative_name
-             }
-            for vote in self.vote_set.all()]
+    hashable_fields = ['dossier', 'title', 'reference', 'kind']
+    def __unicode__(self):
+        return unicode(self.title)
 
 
 class Vote(models.Model):
diff --git a/representatives_votes/serializers.py b/representatives_votes/serializers.py
index 753ed8a..1ad2f50 100644
--- a/representatives_votes/serializers.py
+++ b/representatives_votes/serializers.py
@@ -45,6 +45,7 @@ class ProposalSerializer(serializers.ModelSerializer):
         model = models.Proposal
         fields = (
             'id',
+            'fingerprint',
             'title',
             'description',
             'reference',
@@ -116,6 +117,7 @@ class DossierSerializer(serializers.ModelSerializer):
         model = models.Dossier
         fields = (
             'id',
+            'fingerprint',
             'title',
             'reference',
             'text',
@@ -134,13 +136,6 @@ class DossierListSerializer(DossierSerializer):
                 'url',
             ) + ProposalSerializer.Meta.fields
             
-    '''
-    proposals = serializers.HyperlinkedRelatedField(
-        many=True,
-        read_only=True,
-        view_name='proposal-detail',
-    )
-    '''
     
     proposals = ProposalSerializer(
         many = True,
diff --git a/representatives_votes/utils.py b/representatives_votes/tasks.py
similarity index 50%
rename from representatives_votes/utils.py
rename to representatives_votes/tasks.py
index 71f0d36..a0aff2b 100644
--- a/representatives_votes/utils.py
+++ b/representatives_votes/tasks.py
@@ -18,18 +18,68 @@
 #
 # Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
 
+from __future__ import absolute_import
+
+import logging
+import json
+
+from django.conf import settings
+
+# import redis
+from celery import shared_task
+from urllib2 import urlopen
+
 from representatives_votes.models import Dossier
 from representatives_votes.serializers import DossierDetailSerializer
 
-# Import a dossier
-def import_a_dossier(data):
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+steam_handler = logging.StreamHandler()
+steam_handler.setLevel(logging.DEBUG)
+logger.addHandler(steam_handler)
 
+@shared_task
+def import_a_dossier(data):
+    '''
+    Import a dossier from serialized
+    '''
     serializer = DossierDetailSerializer(data=data)
     if serializer.is_valid():
         serializer.save()
     else:
         print(serializer.errors)
-    
+
+@shared_task
+def import_a_dossier_from_toutatis(fingerprint, delay=False):
+    '''
+    Import a complete dossier from a toutatis server
+    '''
+
+    toutatis_server = getattr(settings,
+                              'TOUTATIS_SERVER',
+                              'http://toutatis.mm.staz.be')
+    search_url = '{server}/api/dossiers/?fingerprint={fingerprint}'.format({
+        'server': toutatis_server,
+        'fingerprint': fingerprint
+    })
+    logger.info('Import dossier from {}'.format(search_url))
+    data = json.load(urlopen(search_url))
+    if data['count'] != 1:
+        raise Exception('Search should return one and only one result')
+    detail_url = data['results'][0]['url']
+    data = json.load(urlopen(detail_url))
+    if delay:
+        import_a_dossier.delay(data)
+    else:
+        import_a_dossier(data)
+
+@shared_task
+def import_a_proposal_from_toutatis(fingerprint, delay=False):
+    '''
+    Import a partial dossier from a toutatis server
+    '''
+    pass    
+
 def import_dossiers(data):
     return [import_a_dossier(d_data) for d_data in data]
 
-- 
GitLab