From c85418c1d887da9f2949eedd9479b6f6861b4031 Mon Sep 17 00:00:00 2001
From: Nicolas Joyard <joyard.nicolas@gmail.com>
Date: Sun, 15 May 2016 08:51:07 +0200
Subject: [PATCH] Add DossierScore view model, publish in API

---
 memopol/api.py                                |  2 +
 representatives_recommendations/api.py        | 24 ++++++++++
 .../migrations/0002_dossierscore.py           | 47 +++++++++++++++++++
 representatives_recommendations/models.py     | 12 +++++
 .../serializers.py                            |  8 ++++
 5 files changed, 93 insertions(+)
 create mode 100644 representatives_recommendations/migrations/0002_dossierscore.py

diff --git a/memopol/api.py b/memopol/api.py
index 6a1fd863..2986d2af 100644
--- a/memopol/api.py
+++ b/memopol/api.py
@@ -14,6 +14,7 @@ from representatives_votes.api import (
 )
 
 from representatives_recommendations.api import (
+    DossierScoreViewSet,
     RecommendationViewSet,
     RepresentativeScoreViewSet,
     ScoredVoteViewSet
@@ -24,6 +25,7 @@ router = routers.DefaultRouter()
 
 router.register(r'constituencies', ConstituencyViewSet)
 router.register(r'dossiers', DossierViewSet)
+router.register(r'dossier_scores', DossierScoreViewSet)
 router.register(r'groups', GroupViewSet)
 router.register(r'mandates', MandateViewSet)
 router.register(r'proposals', ProposalViewSet)
diff --git a/representatives_recommendations/api.py b/representatives_recommendations/api.py
index 3ca026b7..8b4fa191 100644
--- a/representatives_recommendations/api.py
+++ b/representatives_recommendations/api.py
@@ -6,18 +6,42 @@ from rest_framework import (
 from representatives.api import DefaultWebPagination
 
 from .models import (
+    DossierScore,
     Recommendation,
     RepresentativeScore,
     ScoredVote
 )
 
 from .serializers import (
+    DossierScoreSerializer,
     RecommendationSerializer,
     RepresentativeScoreSerializer,
     ScoredVoteSerializer
 )
 
 
+class DossierScoreViewSet(viewsets.ReadOnlyModelViewSet):
+    """
+    API endpoint to view representative score contribution for each dossier
+    """
+    queryset = DossierScore.objects.all()
+    filter_backends = (
+        filters.DjangoFilterBackend,
+        filters.SearchFilter,
+        filters.OrderingFilter
+    )
+    filter_fields = {
+        'id': ['exact'],
+        'dossier': ['exact'],
+        'representative': ['exact'],
+        'score': ['exact', 'gte', 'lte']
+    }
+    search_fields = ('dossier', 'representative')
+    ordering_fields = ('representative', 'dossier')
+    pagination_class = DefaultWebPagination
+    serializer_class = DossierScoreSerializer
+
+
 class RecommendationViewSet(viewsets.ReadOnlyModelViewSet):
     """
     API endpoint that allows recommendations to be viewed.
diff --git a/representatives_recommendations/migrations/0002_dossierscore.py b/representatives_recommendations/migrations/0002_dossierscore.py
new file mode 100644
index 00000000..72ebd2cd
--- /dev/null
+++ b/representatives_recommendations/migrations/0002_dossierscore.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('representatives_recommendations', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='DossierScore',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('score', models.IntegerField(default=0)),
+            ],
+            options={
+                'db_table': 'representatives_recommendations_dossierscores',
+                'managed': False,
+            },
+        ),
+        migrations.RunSQL(
+            """
+            CREATE VIEW "representatives_recommendations_dossierscores"
+            AS SELECT
+                "representatives_votes_vote"."representative_id" || ':' || "representatives_votes_proposal"."dossier_id" AS "id",
+                "representatives_votes_vote"."representative_id",
+                "representatives_votes_proposal"."dossier_id",
+                SUM(CASE WHEN "representatives_votes_vote"."position" = ("representatives_recommendations_recommendation"."recommendation")
+                    THEN "representatives_recommendations_recommendation"."weight"
+                    ELSE (0 - "representatives_recommendations_recommendation"."weight")
+                END) AS "score"
+            FROM "representatives_votes_vote"
+            INNER JOIN "representatives_votes_proposal"
+                ON ( "representatives_votes_vote"."proposal_id" = "representatives_votes_proposal"."id" )
+            LEFT OUTER JOIN "representatives_recommendations_recommendation"
+                ON ( "representatives_votes_proposal"."id" = "representatives_recommendations_recommendation"."proposal_id" )
+            WHERE "representatives_recommendations_recommendation"."id" IS NOT NULL
+            GROUP BY
+                "representatives_votes_vote"."representative_id",
+                "representatives_votes_proposal"."dossier_id"
+            """
+        ),
+    ]
diff --git a/representatives_recommendations/models.py b/representatives_recommendations/models.py
index 3f92d639..49a6ecf5 100644
--- a/representatives_recommendations/models.py
+++ b/representatives_recommendations/models.py
@@ -11,6 +11,18 @@ from representatives_votes.models import Dossier, Proposal, Vote
 from representatives.models import Representative
 
 
+class DossierScore(models.Model):
+    id = models.CharField(max_length=255, primary_key=True)
+    representative = models.ForeignKey(Representative,
+        on_delete=models.DO_NOTHING)
+    dossier = models.ForeignKey(Dossier, on_delete=models.DO_NOTHING)
+    score = models.IntegerField(default=0)
+
+    class Meta:
+        managed = False
+        db_table = 'representatives_recommendations_dossierscores'
+
+
 class RepresentativeScore(models.Model):
     representative = models.OneToOneField('representatives.representative',
         primary_key=True, related_name='score')
diff --git a/representatives_recommendations/serializers.py b/representatives_recommendations/serializers.py
index df42c26e..4a1ae0d0 100644
--- a/representatives_recommendations/serializers.py
+++ b/representatives_recommendations/serializers.py
@@ -1,12 +1,20 @@
 from rest_framework import serializers
 
 from .models import (
+    DossierScore,
     Recommendation,
     RepresentativeScore,
     ScoredVote
 )
 
 
+class DossierScoreSerializer(serializers.HyperlinkedModelSerializer):
+
+    class Meta:
+        model = DossierScore
+        fields = ('representative', 'dossier', 'score')
+
+
 class RecommendationSerializer(serializers.HyperlinkedModelSerializer):
 
     class Meta:
-- 
GitLab