Commit ee22f2c4 authored by Arnaud Fabre's avatar Arnaud Fabre

votes / legislature models

parent e85f36d7
# Django apps
compotista_django-representatives
representatives
chronograph
*.sqlite3
# SASS Cache
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('representatives', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='MemopolGroup',
fields=[
('group', models.OneToOneField(parent_link=True, primary_key=True, serialize=False, to='representatives.Group')),
('active', models.BooleanField(default=False)),
],
options={
},
bases=('representatives.group',),
),
migrations.CreateModel(
name='MemopolRepresentative',
fields=[
('representative_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='representatives.Representative')),
('representative_remote_id', models.CharField(unique=True, max_length=255)),
('score', models.IntegerField(default=0)),
('country', models.ForeignKey(to='representatives.Country', null=True)),
],
options={
},
bases=('representatives.representative',),
),
]
......@@ -22,11 +22,29 @@ from datetime import datetime
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.functional import cached_property
from representatives.models import Representative, Group, Country
from representatives.models import Representative, Mandate, Country
from representatives_votes.models import Vote
from core.utils import create_child_instance_from_parent
class MemopolRepresentative(Representative):
class MemopolRepresentative(models.Model):
# We should link a memopol representative to a representative based
# on the remote_id attribute
parent_identifier = 'remote_id'
child_parent_identifier = 'representative_remote_id'
representative = models.OneToOneField(
Representative,
parent_link=True,
related_name='extra',
null=True,
on_delete=models.SET_NULL
)
representative_remote_id = models.CharField(max_length=255, unique=True)
country = models.ForeignKey(Country, null=True)
......@@ -34,14 +52,23 @@ class MemopolRepresentative(Representative):
def update_score(self):
score = 0
for vote in self.representative.votes.all():
proposal = vote.m_proposal
if proposal.recommendation:
recommendation = proposal.recommendation
if vote.position != recommendation.recommendation:
score -= recommendation.weight
else:
score += recommendation.weight
for vote in self.votes.all():
proposal = vote.proposal
try:
if proposal.recommendation:
recommendation = proposal.recommendation
if ( vote.position != recommendation.recommendation
and (
vote.position == 'abstain' or
recommendation.recommendation == 'abstain' )):
score -= (recommendation.weight / 2)
elif vote.position != recommendation.recommendation:
score -= recommendation.weight
else:
score += recommendation.weight
except Exception:
pass
self.score = score
self.save()
......@@ -66,11 +93,11 @@ class MemopolRepresentative(Representative):
self.save()
# @property
# def votes(self):
# return Vote.objects.filter(
# representative_remote_id = self.remote_id
# )
@cached_property
def votes(self):
return Vote.objects.filter(
representative_remote_id = self.remote_id
)
def active_mandates(self):
return self.mandates.filter(
......@@ -88,20 +115,19 @@ class MemopolRepresentative(Representative):
group__kind='group'
)
class MemopolGroup(Group):
group = models.OneToOneField(
Group,
parent_link = True
)
active = models.BooleanField(default=False)
def update_active(self):
self.active = False
for mandate in self.mandates.all():
if mandate.end_date > datetime.date(datetime.now()):
self.active = True
break
self.save()
@receiver(post_save, sender=Representative)
def create_memopolrepresentative_from_representative(instance, **kwargs):
# create_child_instance_from_parent(MemopolRepresentative, instance)
pass
@receiver(post_save, sender=Mandate)
def update_memopolrepresentative_country(instance, created, **kwargs):
return
if not created:
return
# Update representative country
if instance.group.kind == 'country' and instance.representative.extra.country == None:
instance.representative.extra.update_country()
......@@ -9,6 +9,9 @@
%h1= representative.full_name
%h2
SCORE : {{ representative.extra.score }}
%p
%strong
%a{:href => "{{ representative.current_group_mandate|by_group_url }}"}
......
......@@ -132,8 +132,8 @@ def _render_list(request, representative_list, num_by_page=30):
def groups_by_kind(request, kind):
groups = Group.objects.filter(
kind=kind,
memopolgroup__active=True
)
mandates__end_date__gte=datetime.now()
).distinct().order_by('name')
return render(
request,
......
../django-representatives/representatives
\ No newline at end of file
......@@ -20,16 +20,45 @@
from __future__ import absolute_import
from django.contrib import admin
from django.core.urlresolvers import reverse
from .admin_views import import_vote_with_recommendation, import_vote
from .models import Recommendation, MemopolDossier
from .admin_import_vote import import_vote_with_recommendation, import_vote
from .models import Recommendation
admin.site.register_view('import_vote', view=import_vote)
admin.site.register_view('import_vote_with_recommendation', view=import_vote_with_recommendation)
def link_to_edit(obj, field):
try:
related_obj = getattr(obj, field)
url = reverse(
'admin:{}_{}_change'.format(
related_obj._meta.app_label,
related_obj._meta.object_name.lower()
),
args=(related_obj.pk,)
)
return '&nbsp;<strong><a href="{url}">{obj}</a></strong>'.format(url=url,obj=related_obj)
except:
return '???'
class MemopolDossierAdmin(admin.ModelAdmin):
list_display = ('name', 'dossier')
search_fields = ('name',)
class RecommendationsAdmin(admin.ModelAdmin):
list_display = ('title', 'recommendation', 'proposal', 'weight')
def link_to_proposal(self):
return link_to_edit(self, 'proposal')
link_to_proposal.allow_tags = True
list_display = ('id', 'title', link_to_proposal, 'recommendation','weight')
search_fields = ('title', 'recommendation', 'proposal')
admin.site.register(MemopolDossier, MemopolDossierAdmin)
admin.site.register(Recommendation, RecommendationsAdmin)
......@@ -106,10 +106,9 @@ def import_vote(request):
else:
proposal_id = request.GET.get('import', None)
if proposal_id:
api_url = '{}/api/proposals/{}'.format(toutatis_server, proposal_id)
proposal = requests.get(api_url).json()
# api_url = '{}/api/proposals/{}'.format(toutatis_server, proposal_id)
# proposal = requests.get(api_url).json()
call_command('import_proposal_from_toutatis', proposal_id, interactive=False)
# call_command('update_memopol_votes', proposal['dossier_reference'], interactive=False)
return redirect('/admin/')
......
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from __future__ import absolute_import
from django.conf import settings
from django.shortcuts import render, redirect
from django import forms
from django.core.management import call_command
import requests
from representatives_votes.models import Proposal
from .forms import RecommendationForm
class SearchForm(forms.Form):
query = forms.CharField(label='Search', max_length=100)
def import_vote_with_recommendation(request):
context = {}
toutatis_server = getattr(settings,
'TOUTATIS_SERVER',
'http://toutatis.mm.staz.be')
if request.method == 'POST' and 'search' in request.POST:
form = SearchForm(request.POST)
if form.is_valid():
query = form.cleaned_data['query']
context['api_url'] = '{}/api/proposals/?search={}&limit=1000'.format(
toutatis_server,
query
)
r = requests.get(context['api_url'])
context['results'] = r.json()
elif request.method == 'POST' and 'create_recommendation' in request.POST:
form = RecommendationForm(data=request.POST)
if form.is_valid():
# First import proposal
proposal_id = int(request.POST['proposal_id'])
api_url = '{}/api/proposals/{}'.format(toutatis_server, proposal_id)
proposal = requests.get(api_url).json()
call_command('import_proposal_from_toutatis', proposal_id, interactive=False)
# call_command('update_memopol_votes', proposal['dossier_reference'], interactive=False)
memopol_proposal = Proposal.objects.get(
title = proposal['title'],
datetime = proposal['datetime'],
kind = proposal['kind'],
)
recommendation = form.save(commit=False)
recommendation.proposal = memopol_proposal
recommendation.save()
return redirect('/admin/votes/recommendation/')
else:
proposal_id = request.GET.get('import', None)
if proposal_id:
api_url = '{}/api/proposals/{}'.format(toutatis_server, proposal_id)
proposal = requests.get(api_url).json()
context['recommendation_proposal_title'] = proposal['title']
context['recommendation_proposal_dossier_title'] = proposal['dossier_title']
context['recommendation_proposal_id'] = proposal_id
context['recommendation_form'] = RecommendationForm()
form = SearchForm()
context['form'] = form
return render(request, 'votes/admin/import.html', context)
def import_vote(request):
context = {}
toutatis_server = getattr(settings,
'TOUTATIS_SERVER',
'http://toutatis.mm.staz.be')
if request.method == 'POST' and 'search' in request.POST:
print(request.POST)
form = SearchForm(request.POST)
if form.is_valid():
query = form.cleaned_data['query']
context['api_url'] = '{}/api/proposals/?search={}&limit=1000'.format(
toutatis_server,
query
)
r = requests.get(context['api_url'])
context['results'] = r.json()
else:
proposal_id = request.GET.get('import', None)
if proposal_id:
# api_url = '{}/api/proposals/{}'.format(toutatis_server, proposal_id)
# proposal = requests.get(api_url).json()
call_command('import_proposal_from_toutatis', proposal_id, interactive=False)
# call_command('update_memopol_votes', proposal['dossier_reference'], interactive=False)
return redirect('/admin/')
form = SearchForm()
context['form'] = form
return render(request, 'votes/admin/import.html', context)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import core.fields
class Migration(migrations.Migration):
dependencies = [
('representatives_votes', '0002_auto_20150616_1249'),
('votes', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='memopoldossier',
name='dossier_ptr',
),
migrations.AddField(
model_name='memopoldossier',
name='dossier',
field=core.fields.AutoOneToOneField(primary_key=True, default=0, serialize=False, to='representatives_votes.Dossier'),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('votes', '0002_auto_20150616_1516'),
]
operations = [
migrations.AlterField(
model_name='memopoldossier',
name='description',
field=models.TextField(default=b'', blank=True),
preserve_default=True,
),
migrations.AlterField(
model_name='memopoldossier',
name='name',
field=models.CharField(default=b'', max_length=1000, blank=True),
preserve_default=True,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import core.fields
class Migration(migrations.Migration):
dependencies = [
('votes', '0003_auto_20150616_1523'),
]
operations = [
migrations.AlterField(
model_name='memopoldossier',
name='dossier',
field=core.fields.AutoOneToOneField(parent_link=True, related_name='extra', primary_key=True, serialize=False, to='representatives_votes.Dossier'),
preserve_default=True,
),
]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('votes', '0004_auto_20150616_1527'),
]
operations = [
migrations.AddField(
model_name='memopoldossier',
name='dossier_reference',
field=models.CharField(default='', max_length=200),
preserve_default=False,
),
migrations.AlterField(
model_name='memopoldossier',
name='dossier',
field=models.OneToOneField(parent_link=True, related_name='extra', primary_key=True, serialize=False, to='representatives_votes.Dossier'),
preserve_default=True,
),
]
......@@ -19,16 +19,21 @@
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from django.db import models
from django.utils.functional import cached_property
from django.db.models.signals import post_save
from django.dispatch import receiver
from representatives_votes.models import Vote, Proposal, Dossier
from legislature.models import MemopolRepresentative
from core.utils import create_child_instance_from_parent
from .tasks import update_representatives_score_for_proposal
class Recommendation(models.Model):
SCORE_TABLE = {
('abstain', 'abstain'): 1,
('abstain', 'for'): -0.5,
('abstain', 'against'): -0.5,
}
VOTECHOICES = (
......@@ -36,27 +41,54 @@ class Recommendation(models.Model):
('for', 'for'),
('against', 'against')
)
proposal = models.OneToOneField(
Proposal,
related_name='recommendation'
)
recommendation = models.CharField(max_length=10, choices=VOTECHOICES)
title = models.CharField(max_length=1000, blank=True)
description = models.TextField(blank=True)
weight = models.IntegerField(default=0)
@receiver(post_save, sender=Recommendation)
def update_score(instance, **kwargs):
update_representatives_score_for_proposal(instance.proposal)
class MemopolDossier(Dossier):
name = models.CharField(max_length=1000)
description = models.TextField(blank=True)
parent_identifier = 'reference'
child_parent_identifier = 'dossier_reference'
dossier = models.OneToOneField(
Dossier,
primary_key=True,
parent_link=True,
related_name='extra'
)
dossier_reference = models.CharField(max_length=200)
name = models.CharField(max_length=1000, blank=True, default='')
description = models.TextField(blank=True, default='')
def save(self, *args, **kwargs):
if not self.name:
self.name = self.dossier.title
return super(MemopolDossier, self).save(*args, **kwargs)
@receiver(post_save, sender=Dossier)
def create_memopolrepresentative_from_representative(instance, **kwargs):
create_child_instance_from_parent(MemopolDossier, instance)
class MemopolVote(Vote):
class Meta:
proxy = True
@property
@cached_property
def representative(self):
return MemopolRepresentative.objects.get(
remote_id = self.representative_remote_id
......
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from __future__ import absolute_import
from celery import shared_task
from legislature.models import MemopolRepresentative
from django.db.models import get_model
@shared_task
def update_representatives_score():
'''
Update score for all representatives
'''
for representative in MemopolRepresentative.objects.all():
representative.update_score()
@shared_task
def update_representatives_score_for_proposal(proposal):
'''
Update score for representatives that have votes for proposal
'''
MemopolVote = get_model('votes', 'MemopolVote')
for vote in MemopolVote.objects.filter(proposal_id = proposal.id):
# Extra is the MemopolRepresentative object
vote.representative.extra.update_score()
......@@ -8,7 +8,7 @@
%form{:action => '', :method => 'post'}
- csrf_token
{{ form }}.
{{ form }}
%input{:type => 'submit', :value => 'Search', :name => 'search'}
- if results
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment