Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Anthony
memopol
Commits
ee22f2c4
Commit
ee22f2c4
authored
Jul 01, 2015
by
Arnaud Fabre
Browse files
votes / legislature models
parent
e85f36d7
Changes
17
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
ee22f2c4
# Django apps
compotista_django-representatives
representatives
chronograph
*.sqlite3
# SASS Cache
...
...
legislature/migrations/0001_initial.py
deleted
100644 → 0
View file @
e85f36d7
# -*- 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'
,),
),
]
legislature/migrations/__init__.py
deleted
100644 → 0
View file @
e85f36d7
legislature/models.py
View file @
ee22f2c4
...
...
@@ -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
()
legislature/templates/legislature/representative_view.haml
View file @
ee22f2c4
...
...
@@ -9,6 +9,9 @@
%h1
=
representative
.
full_name
%h2
SCORE : {{ representative.extra.score }}
%p
%strong
%a
{
:href
=>
"{{ representative.current_group_mandate|by_group_url }}"
}
...
...
legislature/views.py
View file @
ee22f2c4
...
...
@@ -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
,
m
emopolgroup__active
=
True
)
m
andates__end_date__gte
=
datetime
.
now
()
)
.
distinct
().
order_by
(
'name'
)
return
render
(
request
,
...
...
representatives
0 → 120000
View file @
ee22f2c4
../django-representatives/representatives
\ No newline at end of file
votes/admin.py
View file @
ee22f2c4
...
...
@@ -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
' <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
)
votes/admin_
import_vote
.py
→
votes/admin_
views
.py
View file @
ee22f2c4
...
...
@@ -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/'
)
...
...
votes/admin_views_flymake.py
0 → 100644
View file @
ee22f2c4
# 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
)
votes/migrations/0002_auto_20150616_1516.py
0 → 100644
View file @
ee22f2c4
# -*- 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
,
),
]
votes/migrations/0003_auto_20150616_1523.py
0 → 100644
View file @
ee22f2c4
# -*- 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
,
),
]
votes/migrations/0004_auto_20150616_1527.py
0 → 100644
View file @
ee22f2c4
# -*- 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
,
),
]
votes/migrations/0005_auto_20150617_1243.py
0 → 100644
View file @
ee22f2c4
# -*- 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
,
),
]
votes/models.py
View file @
ee22f2c4
...
...
@@ -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
...
...
votes/tasks.py
0 → 100644
View file @
ee22f2c4
# 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
()
votes/templates/votes/admin/import.haml
View file @
ee22f2c4
...
...
@@ -8,7 +8,7 @@
%form
{
:action
=>
''
,
:method
=>
'post'
}
-
csrf_token
{{ form }}
.
{{ form }}
%input
{
:type
=>
'submit'
,
:value
=>
'Search'
,
:name
=>
'search'
}
-
if
results
...
...