From 9eaea806b4fcd109d515e9c7b86c2a48af2df484 Mon Sep 17 00:00:00 2001
From: Arnaud Fabre <arnaud.fabre@camobscura.fr>
Date: Mon, 6 Jul 2015 18:27:53 +0200
Subject: [PATCH] better api

---
 .../commands/import_dossier_from_toutatis.py  |   4 +-
 representatives_votes/models.py               |   4 +-
 representatives_votes/serializers.py          | 144 ++++++------------
 representatives_votes/tasks.py                |  45 ++----
 requirements.txt                              |   2 +-
 5 files changed, 65 insertions(+), 134 deletions(-)

diff --git a/representatives_votes/management/commands/import_dossier_from_toutatis.py b/representatives_votes/management/commands/import_dossier_from_toutatis.py
index de8076a..14eb2cd 100644
--- a/representatives_votes/management/commands/import_dossier_from_toutatis.py
+++ b/representatives_votes/management/commands/import_dossier_from_toutatis.py
@@ -34,6 +34,6 @@ class Command(BaseCommand):
     def handle(self, *args, **options):
         fingerprint = options['fingerprint']
         if options['celery']:
-            import_a_dossier_from_toutatis.delay(fingerprint, delay=True)
+            import_a_dossier_from_toutatis.delay(fingerprint)
         else:
-            import_a_dossier_from_toutatis(fingerprint, delay=False)
+            import_a_dossier_from_toutatis(fingerprint)
diff --git a/representatives_votes/models.py b/representatives_votes/models.py
index 0bfe1ce..08fdf8c 100644
--- a/representatives_votes/models.py
+++ b/representatives_votes/models.py
@@ -43,7 +43,9 @@ class Proposal(HashableModel, TimeStampedModel):
     total_against = models.IntegerField()
     total_for = models.IntegerField()
 
-    hashable_fields = ['dossier', 'title', 'reference', 'kind']
+    hashable_fields = ['dossier', 'title', 'reference',
+                       'kind', 'total_abstain', 'total_against',
+                       'total_for']
 
     def __unicode__(self):
         return unicode(self.title)
diff --git a/representatives_votes/serializers.py b/representatives_votes/serializers.py
index 1ad2f50..82ab573 100644
--- a/representatives_votes/serializers.py
+++ b/representatives_votes/serializers.py
@@ -25,27 +25,39 @@ from django.db import transaction
 
 
 class VoteSerializer(serializers.ModelSerializer):
-    '''
-    Serializer for votes
-    '''
+    proposal = serializers.CharField(
+        source='proposal.fingerprint'
+    )
+    
     class Meta:
         model = models.Vote
         fields = (
+            'id',
+            'proposal',
             'representative_name',
             'representative_remote_id',
             'position'
         )
 
+    def to_internal_value(self, data):
+        data = super(VoteSerializer, self).to_internal_value(data)
+        data['proposal'] = models.Proposal.objects.get(
+            fingerprint=data['proposal']['fingerprint']
+        )
+        return data
+
 
 class ProposalSerializer(serializers.ModelSerializer):
-    '''
-    Base Proposal Serializer
-    '''
+    dossier = serializers.CharField(
+        source='dossier.fingerprint'
+    )
+    
     class Meta:
         model = models.Proposal
         fields = (
             'id',
             'fingerprint',
+            'dossier',
             'title',
             'description',
             'reference',
@@ -56,63 +68,47 @@ class ProposalSerializer(serializers.ModelSerializer):
             'total_for',
         )
 
-
-class ProposalHyperLinkedSerializer(ProposalSerializer):
-    '''
-    Proposal Serializer with hyperlink to dossier (used for listing)
-    '''
-    dossier = serializers.HyperlinkedRelatedField(
-        read_only = True,
-        view_name = 'dossier-detail',
-    )
-    
-    dossier_title = serializers.CharField(
-        read_only = True,
-        source = 'dossier.title'
-    )
-
-    dossier_reference = serializers.CharField(
-        read_only = True,
-        source = 'dossier.reference'
-    )
-    
-    class Meta(ProposalSerializer.Meta):
-        fields = ProposalSerializer.Meta.fields + (
-            'dossier',
-            'dossier_title',
-            'dossier_reference',
-            'url',
+    def to_internal_value(self, data):
+        validated_data = super(ProposalSerializer, self).to_internal_value(data)
+        validated_data['dossier'] = models.Dossier.objects.get(
+            fingerprint=validated_data['dossier']['fingerprint']
         )
+        validated_data['votes'] = data['votes']
+        return validated_data
+
+    def _create_votes(self, votes_data, proposal):
+        for vote in votes_data:
+            vote['proposal'] = proposal
+            models.Vote.objects.create(
+                **vote
+            )
         
-class ProposalDetailSerializer(ProposalSerializer):
-    '''
-    Proposal Serializer with votes detail (used in Dossier Detail)
-    '''
-    votes = VoteSerializer(many=True)
-    
-    class Meta(ProposalSerializer.Meta):
-        fields = ProposalSerializer.Meta.fields + (
-            'votes',
+    def create(self, validated_data):
+        votes_data = validated_data.pop('votes')
+        proposal = models.Proposal.objects.create(
+            **validated_data
         )
+        self._create_votes(votes_data, proposal)
+        return proposal
 
+    def update(self, instance, validated_data):
+        validated_data.pop('votes')
+        for attr, value in validated_data.iteritems():
+            setattr(instance, attr, value)
+        instance.save()
+        return instance
 
-class ProposalDetailHyperLinkedSerializer(ProposalDetailSerializer, ProposalHyperLinkedSerializer):
-    '''
-    Proposal Serializer combined Detail Serializer and Hyperlinked Serializer
-    '''
+
+class ProposalDetailSerializer(ProposalSerializer):
+    votes = VoteSerializer(many=True)
+    
     class Meta(ProposalSerializer.Meta):
         fields = ProposalSerializer.Meta.fields + (
-            'dossier',
-            'dossier_title',
-            'dossier_reference',
             'votes',
         )
 
 
 class DossierSerializer(serializers.ModelSerializer):
-    '''
-    Base Dossier Serializer
-    '''
     class Meta:
         model = models.Dossier
         fields = (
@@ -122,30 +118,7 @@ class DossierSerializer(serializers.ModelSerializer):
             'reference',
             'text',
             'link',
-        )
-
-
-class DossierListSerializer(DossierSerializer):
-    '''
-    Dossier Serializer with short description of proposals
-    '''
-    class ProposalSerializer(ProposalSerializer):
-        class Meta(ProposalSerializer.Meta):
-            fields = (
-                'id',
-                'url',
-            ) + ProposalSerializer.Meta.fields
-            
-    
-    proposals = ProposalSerializer(
-        many = True,
-        read_only = True
-    )
-
-    class Meta(DossierSerializer.Meta):
-        fields = DossierSerializer.Meta.fields + (
             'url',
-            'proposals',
         )
 
 
@@ -162,26 +135,3 @@ class DossierDetailSerializer(DossierSerializer):
         fields = DossierSerializer.Meta.fields + (
             'proposals',
         )
-
-    @transaction.atomic
-    def create(self, validated_data):
-        proposals_data = validated_data.pop('proposals')
-        dossier, _ = models.Dossier.objects.get_or_create(**validated_data)
-
-        for proposal_data in proposals_data:
-            proposal, created = self._create_proposal(
-                proposal_data,
-                dossier
-            )
-
-        return dossier
-    
-    def _create_proposal(self, proposal_data, dossier):
-        votes_data = proposal_data.pop('votes')
-        proposal_data['dossier'] = dossier
-        proposal, created = models.Proposal.objects.get_or_create(**proposal_data)
-        if created:
-            for vote_data in votes_data:
-                vote_data['proposal'] = proposal
-                models.Vote.objects.create(**vote_data)
-        return (proposal, created)
diff --git a/representatives_votes/tasks.py b/representatives_votes/tasks.py
index b62b1fa..630564e 100644
--- a/representatives_votes/tasks.py
+++ b/representatives_votes/tasks.py
@@ -29,43 +29,22 @@ import redis
 from celery import shared_task
 from urllib2 import urlopen
 
-from representatives_votes.models import Dossier
-from representatives_votes.serializers import DossierDetailSerializer
+from representatives_votes.models import Dossier, Proposal, Vote
+from representatives.tasks import import_a_model
+from representatives_votes.serializers import DossierSerializer, ProposalSerializer, VoteSerializer
 
 logger = logging.getLogger(__name__)
 
 @shared_task
-def import_a_dossier(data):
-    '''
-    Import a dossier from serialized
-    '''
-    with redis.Redis().lock('import_a_dossier'):
-        try:
-            dossier = Dossier.objects.get(
-                fingerprint=data['fingerprint']
-            )
-            serializer = DossierDetailSerializer(
-                instance=dossier,
-                data=data
-            )
-        except:
-            serializer = DossierDetailSerializer(data=data)
-
-        if serializer.is_valid():
-            return serializer.save()
-        else:
-            raise Exception(serializer.errors)           
-
-@shared_task
-def import_a_dossier_from_toutatis(fingerprint, delay=False):
+def import_a_dossier_from_toutatis(fingerprint):
     '''
     Import a complete dossier from a toutatis server
     '''
     toutatis_server = settings.TOUTATIS_SERVER
-    search_url = '{server}/api/dossiers/?fingerprint={fingerprint}'.format({
-        'server': toutatis_server,
-        'fingerprint': fingerprint
-    })
+    search_url = '{}/api/dossiers/?fingerprint={}'.format(
+        toutatis_server,
+        fingerprint
+    )
     logger.info('Import dossier with fingerprint {} from {}'.format(
         fingerprint,
         search_url
@@ -75,10 +54,10 @@ def import_a_dossier_from_toutatis(fingerprint, delay=False):
         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)
+    import_a_model(data, Dossier, DossierSerializer)
+    for proposal in data['proposals']:
+        logger.info('Import proposal {}'.format(proposal['title']))
+        import_a_model(proposal, Proposal, ProposalSerializer)
 
 @shared_task
 def import_a_proposal_from_toutatis(fingerprint, delay=False):
diff --git a/requirements.txt b/requirements.txt
index f7aa55b..ca5b397 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-django>=1.7,<1.8
+django>=1.8,<1.9
 djangorestframework
-- 
GitLab