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