Commit 1eb28ac2 authored by Alexandre's avatar Alexandre

feature: risefor-mobilisation setup

parent 4eeb9d8a
......@@ -10,11 +10,11 @@ __pycache__
/_import/elected_specimen.json
database_clean2.xls
db.sqlite3
/united4earth/settings.py
/united4earth/settings_local.py
/united4earth/settings_pp.py
/united4earth/settings_prod.py
/united4earth/static/css/*
/risefor-mobilisation/settings.py
/risefor-mobilisation/settings_local.py
/risefor-mobilisation/settings_pp.py
/risefor-mobilisation/settings_prod.py
/risefor-mobilisation/static/css/*
/nbproject/*
# Risefor.org
Risefor.org is an open-source tool designed to allow citizens to exchange, organise and act upon their elected officials.
Using Risefor.org, citizens can gather around a common topic of interest and advocate for an agreed upon position to their elected officials.
Risefor.org is an open-source tool designed to allow citizens to exchange, organise and act.
Using Risefor.org, citizens can gather around a common topic of interest and advocate for an agreed upon position to the rest of society.
## Installation
git clone https://git.happy-dev.fr/startinblox/applications/united4earth.git
git clone https://git.happy-dev.fr/startinblox/applications/risefor-mobilisation.git
python3 -m venv env # optional venv
. env/bin/activate # optional venv
pip3 install -r requirements.txt
cd united4earth
cd risefor-mobilisation
cp settings_sample.py settings.py
cd ..
python3 manage.py migrate
python3 manage.py import_elected
python3 manage.py createsuperuser
## Update CSS
npm install -g sass
cd united4earth/static
sass --watch scss/index.scss css/united4earth.css
sass --watch risefor-mobilisation/static/scss/index.scss risefor-mobilisation/static/css/risefor-mobilisation.css
## Launch server
source env/bin/activate # optional venv
......
from django.contrib import admin
from guardian.admin import GuardedModelAdmin
from .models import Representative, Phone, LoomioTopic, ActionGroup, ActionGroupFrequency, ActionGroupTheme, Event, UserProfile
from .models import ResourceLink, ActionGroup, ActionGroupFrequency, ActionGroupTheme, UserProfile
class PhoneInline(admin.TabularInline):
model = Phone
extra = 1
class RepresentativeAdmin(admin.ModelAdmin):
inlines = [ PhoneInline, ]
admin.site.register(Representative, RepresentativeAdmin)
admin.site.register(Phone, GuardedModelAdmin)
admin.site.register(ActionGroup, GuardedModelAdmin)
admin.site.register(LoomioTopic, GuardedModelAdmin)
admin.site.register(Event, GuardedModelAdmin)
admin.site.register(ResourceLink, GuardedModelAdmin)
admin.site.register(ActionGroupFrequency, GuardedModelAdmin)
admin.site.register(ActionGroupTheme, GuardedModelAdmin)
admin.site.register(UserProfile, GuardedModelAdmin)
from django.core.management.base import BaseCommand
from data_manager.models import Representative
from django.db.models import Value
from django.db.models.functions import Concat
from csv import DictReader
import csv
class Command(BaseCommand):
help = 'Inserts additionnal information to representatives'
def import_csv(self, file_name):
with open(file_name) as csvFile:
csvReader = DictReader(csvFile)
representatives = Representative.objects.annotate(search_name=Concat('firstname', Value(' '), 'lastname'))
for row in csvReader:
try:
representative = representatives.get(search_name__iexact=row['name'])
representative.permanent_commission = row['permanent_commission']
representative.parliamentary_missions = row['parliamentary_missions']
representative.additional_functions = row['additional_functions']
representative.study_groups = row['study_groups']
representative.photo = row['photo']
if 'website' in row :
representative.website = row['website']
representative.save()
except Representative.DoesNotExist:
print(row['name'] + ' not found in database')
def handle(self, *args, **options):
self.import_csv('elected_officials_additional_data.csv')
self.import_csv('elected_officials_senators_additional_data.csv')
from django.core.management.base import BaseCommand
from data_manager.models import Representative, Phone
from django.conf import settings
from csv import DictReader
import csv
class Command(BaseCommand):
help = 'Imports representatives'
def import_csv(self, options, file_name):
with open(file_name) as csvFile:
csvReader = DictReader(csvFile)
i = 0
for row in csvReader:
already_created = Representative.objects.filter(firstname__iexact = row["PRENOM"], lastname__iexact = row["NOM"]).exists()
if not already_created :
representative = Representative()
representative.civility = row["CIVILITE"].capitalize()
representative.firstname = row["PRENOM"].capitalize()
representative.lastname = row["NOM"].capitalize()
representative.function = row["FONCTION"].capitalize()
representative.title = row["TITRE"].capitalize()
representative.protocol_role = row["PROTOCOLE ROLE"].capitalize()
representative.protocol_person = row["PROTOCOLE PERSONNE"].capitalize()
representative.political_party = row["PARTI"].capitalize()
representative.election_department = row["DEPARTEMENT ELECTION"].capitalize()
representative.election_region = row["REGION ELECTION"].capitalize()
representative.phone_organization = row["TEL ORGANISME"]
representative.email_direct = row["EMAIL DIRECT"]
representative.email_organization = row["EMAIL ORGANISME"]
representative.address1 = row["ADRESSE 1"]
representative.address2 = row["ADRESSE 2"]
representative.postal_code = row["CP"]
representative.city = row["VILLE"].capitalize()
representative.country = row["PAYS"].capitalize()
representative.website = row["WEB"]
representative.organization_type = row["TYPE"].capitalize()
representative.organization_subtype = row["SOUS TYPE"].capitalize()
if "MME" in row["CIVILITE"]:
representative.photo = settings.SITE_URL + "static/img/avatar_f.png"
else:
representative.photo = settings.SITE_URL + "static/img/avatar_h.png"
representative.save()
if row["TEL DIRECT"] :
phone = Phone()
phone.number = row["TEL DIRECT"]
phone.representative = representative
phone.save()
i = i + 1
if options['dev'] and i > 10 :
break
def add_arguments(self, parser):
parser.add_argument(
'--dev',
action='store_true',
help='Dummy data for development',
)
def handle(self, *args, **options):
self.import_csv(options=options, file_name='elected_officials.csv')
from django.core.management.base import BaseCommand
from data_manager.models import Representative, Phone
from django.conf import settings
from csv import DictReader
import csv
class Command(BaseCommand):
help = 'Imports senators'
def handle(self, *args, **options):
with open('elected_officials_senators.csv') as csvFile:
csvReader = DictReader(csvFile)
for row in csvReader:
if not row["PRENOM"] :
continue
already_created = Representative.objects.filter(firstname__iexact = row["PRENOM"], lastname__iexact = row["NOM"])
if not already_created.exists() :
representative = Representative()
else :
representative = already_created[0]
print(row["PRENOM"]+ " " +row["NOM"] +" already is already registered in the database.")
representative.civility = row["CIVILITE"].capitalize()
representative.firstname = row["PRENOM"].capitalize()
representative.lastname = row["NOM"].capitalize()
representative.function = row["FONCTION"].capitalize()
representative.title = row["TITRE"].capitalize()
representative.protocol_role = row["PROTOCOLE ROLE"].capitalize()
representative.protocol_person = row["PROTOCOLE PERSONNE"].capitalize()
representative.political_party = row["PARTI"].capitalize()
representative.election_department = row["DEPARTEMENT ELECTION"].capitalize()
representative.election_region = row["REGION ELECTION"].capitalize()
representative.email_direct = row["EMAIL DIRECT"]
representative.email_organization = row["EMAIL ORGANISME"]
representative.address1 = row["ADRESSE 1"]
representative.address2 = row["ADRESSE 2"]
representative.postal_code = row["CP"] if row['CP'] else None
representative.city = row["VILLE"].capitalize()
representative.country = row["PAYS"].capitalize()
representative.website = row["WEB"]
representative.organization_type = row["TYPE"].capitalize()
representative.organization_subtype = row["SOUS TYPE"].capitalize()
if "MME" in row["CIVILITE"]:
representative.photo = settings.SITE_URL + "static/img/avatar_f.png"
else:
representative.photo = settings.SITE_URL + "static/img/avatar_h.png"
representative.save()
for i in range(1, 4):
type = row["TEL"+ str(i) +" TYPE"]
tel = row["TEL"+ str(i)]
if type in ["Principal", "Parl."] and tel and not tel.startswith('06') and not tel.startswith('07') :
phone = Phone()
phone.number = tel
phone.representative = representative
phone.save()
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-08 09:26
# Generated by Django 1.11.20 on 2019-06-11 17:47
from __future__ import unicode_literals
from django.conf import settings
......@@ -21,16 +21,19 @@ class Migration(migrations.Migration):
name='ActionGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('name', models.CharField(max_length=255, verbose_name='Nom du groupe')),
('objective', models.TextField(verbose_name='Objectif')),
('description', models.TextField(blank=True, null=True, verbose_name='Description')),
('place', models.TextField(blank=True, null=True, verbose_name='Lieu')),
('image', models.URLField(blank=True, verbose_name='Image')),
('slug', models.SlugField(blank=True, null=True, unique=True)),
('actiongroups', models.ManyToManyField(blank=True, related_name='_actiongroup_actiongroups_+', to='data_manager.ActionGroup')),
('author', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='created_groups', to=settings.AUTH_USER_MODEL)),
('conversation', models.ManyToManyField(blank=True, to='djangoldp_conversation.Conversation')),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='ActionGroupFrequency',
......@@ -47,85 +50,21 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name="Nom de l'évenement")),
('image', models.URLField(blank=True, verbose_name='Image')),
('date', models.DateField(blank=True, null=True, verbose_name="Date de l'action")),
('location', models.CharField(blank=True, max_length=255, null=True, verbose_name='Lieu')),
('theme', models.CharField(blank=True, max_length=255, null=True, verbose_name='Theme')),
('organization', models.CharField(blank=True, max_length=255, null=True, verbose_name='Organisation')),
('description', models.TextField(blank=True, null=True, verbose_name='Description')),
('actiongroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='data_manager.ActionGroup')),
],
options={
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='LoomioTopic',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='Titre du sujet')),
('url', models.URLField(blank=True, null=True, verbose_name='Loomio')),
],
),
migrations.CreateModel(
name='Phone',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='Numéro de téléphone')),
],
),
migrations.CreateModel(
name='Representative',
name='ResourceLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('civility', models.CharField(blank=True, max_length=100, null=True, verbose_name='Civilité')),
('firstname', models.CharField(blank=True, max_length=100, null=True, verbose_name='Prénom')),
('lastname', models.CharField(blank=True, max_length=100, null=True, verbose_name='Nom')),
('function', models.CharField(blank=True, max_length=255, null=True, verbose_name='Fonction')),
('title', models.CharField(blank=True, max_length=255, null=True, verbose_name='Titre')),
('protocol_role', models.CharField(blank=True, max_length=100, null=True, verbose_name='Rôle protocolaire')),
('protocol_person', models.CharField(blank=True, max_length=100, null=True, verbose_name='Personne protocolaire')),
('political_party', models.CharField(blank=True, max_length=100, null=True, verbose_name='Parti politique')),
('election_department', models.CharField(blank=True, max_length=100, null=True, verbose_name="Département d'élection")),
('election_region', models.CharField(blank=True, max_length=100, null=True, verbose_name="Région d'élection")),
('phone_organization', models.CharField(blank=True, max_length=50, null=True, verbose_name='Tél. organisation')),
('email_direct', models.EmailField(blank=True, max_length=100, null=True, verbose_name='Email direct')),
('email_organization', models.EmailField(blank=True, max_length=100, null=True, verbose_name='Email Organisation')),
('address1', models.CharField(blank=True, max_length=255, null=True, verbose_name='Adresse 1')),
('address2', models.CharField(blank=True, max_length=255, null=True, verbose_name='Adresse 2')),
('postal_code', models.CharField(blank=True, max_length=255, null=True, verbose_name='Code postal')),
('city', models.CharField(blank=True, max_length=150, null=True, verbose_name='Ville')),
('country', models.CharField(blank=True, max_length=150, null=True, verbose_name='Pays')),
('website', models.CharField(blank=True, max_length=150, null=True, verbose_name='Site web')),
('organization_type', models.CharField(blank=True, max_length=150, null=True, verbose_name="Type d'organisation")),
('organization_subtype', models.CharField(blank=True, max_length=150, null=True, verbose_name="Sous-type d'organisation")),
('photo', models.URLField(verbose_name='Photo')),
('slug', models.SlugField(blank=True, null=True, unique=True)),
('permanent_commission', models.CharField(blank=True, max_length=255, verbose_name='Commission permanente')),
('parliamentary_missions', models.TextField(blank=True, verbose_name='Missions parlementaires')),
('additional_functions', models.TextField(blank=True, verbose_name='Fonctions supplémentaires')),
('study_groups', models.TextField(blank=True, verbose_name="Groupes d'étude")),
('title', models.CharField(max_length=255, verbose_name='Texte du lien')),
('url', models.URLField(blank=True, null=True, verbose_name='URL')),
],
options={
'ordering': ['lastname'],
},
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('photo', models.URLField(blank=True, verbose_name='Photo')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='userprofile', to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur')),
],
),
migrations.AddField(
model_name='phone',
name='representative',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='direct_phones', to='data_manager.Representative', verbose_name='Élu'),
),
migrations.AddField(
model_name='actiongroup',
name='frequency',
......@@ -138,17 +77,12 @@ class Migration(migrations.Migration):
),
migrations.AddField(
model_name='actiongroup',
name='loomios',
field=models.ManyToManyField(blank=True, to='data_manager.LoomioTopic'),
name='links',
field=models.ManyToManyField(blank=True, to='data_manager.ResourceLink'),
),
migrations.AddField(
model_name='actiongroup',
name='members',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='actiongroup',
name='representatives',
field=models.ManyToManyField(blank=True, related_name='action_groups', to='data_manager.Representative'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-06-04 01:26
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('data_manager', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='representative',
options={'ordering': ['lastname']},
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-08 15:26
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('data_manager', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='user',
field=models.OneToOneField(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur'),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-08 15:31
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('data_manager', '0002_userprofile_user'),
]
operations = [
migrations.AlterField(
model_name='userprofile',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='userprofile', to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-06-04 01:26
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('data_manager', '0002_auto_20190604_0326'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='user',
field=models.OneToOneField(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur'),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-08 16:46
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('data_manager', '0003_auto_20190608_1731'),
]
operations = [
migrations.AlterModelOptions(
name='actiongroup',
options={'ordering': ['created_at']},
),
migrations.AddField(
model_name='actiongroup',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-09 09:56
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('data_manager', '0003_userprofile_user'),
('data_manager', '0004_auto_20190608_1846'),
]
operations = [
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.21 on 2019-06-09 10:11
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('data_manager', '0005_merge_20190609_1156'),
]
operations = [
migrations.AlterModelOptions(
name='actiongroup',
options={'ordering': ['-created_at']},
),
migrations.AlterField(
model_name='userprofile',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='userprofile', to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur'),
),
]
......@@ -5,7 +5,6 @@ from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from guardian.shortcuts import assign_perm
from datetime import datetime
from django.utils.text import slugify
from djangoldp_conversation.models import Conversation, Message
from .permissions import ThreadPermission
from djangoldp.models import Model
......@@ -31,65 +30,9 @@ class UserProfile(Model):
return self.user.first_name +" "+ self.user.last_name
class Representative(Model):
civility = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Civilité")
firstname = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Prénom")
lastname = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Nom")
function = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"Fonction")
title = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"Titre")
protocol_role = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Rôle protocolaire")
protocol_person = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Personne protocolaire")
political_party = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Parti politique")
election_department = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Département d'élection")
election_region = models.CharField(max_length=100, blank=True, null=True, verbose_name=u"Région d'élection")
phone_organization = models.CharField(max_length=50, blank=True, null=True, verbose_name=u"Tél. organisation")
email_direct = models.EmailField(max_length=100, blank=True, null=True, verbose_name=u"Email direct")
email_organization = models.EmailField(max_length=100, blank=True, null=True, verbose_name=u"Email Organisation")
address1 = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"Adresse 1")
address2 = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"Adresse 2")
postal_code = models.CharField(max_length=255, blank=True, null=True, verbose_name=u"Code postal")
city = models.CharField(max_length=150, blank=True, null=True, verbose_name=u"Ville")
country = models.CharField(max_length=150, blank=True, null=True, verbose_name=u"Pays")
website = models.CharField(max_length=150, blank=True, null=True, verbose_name=u"Site web")
organization_type = models.CharField(max_length=150, blank=True, null=True, verbose_name=u"Type d'organisation")
organization_subtype = models.CharField(max_length=150, blank=True, null=True, verbose_name=u"Sous-type d'organisation")
photo = models.URLField(verbose_name=u"Photo")
slug = models.SlugField(blank=True, null=True, unique=True)
permanent_commission = models.CharField(max_length=255, blank=True, verbose_name=u"Commission permanente")
parliamentary_missions = models.TextField(blank=True, verbose_name=u"Missions parlementaires")
additional_functions = models.TextField(blank=True, verbose_name=u"Fonctions supplémentaires")
study_groups = models.TextField(blank=True, verbose_name=u"Groupes d'étude")
def get_full_name(self):
return "{} {}".format(self.firstname, self.lastname)
class Meta:
rdf_context = {"get_full_name": "rdfs:label"}
serializer_fields = ['@id', 'civility', 'firstname', 'lastname', 'get_full_name', 'function', 'title', 'protocol_role', 'protocol_person', 'political_party', 'election_department', 'election_region', 'direct_phones', 'phone_organization', 'email_direct', 'email_organization', 'address1', 'address2', 'postal_code', 'city', 'country', 'website', 'organization_type', 'organization_subtype', 'photo', 'slug', 'permanent_commission', 'additional_functions', 'parliamentary_missions', 'study_groups']
depth = 0
ordering=['lastname']
def __str__(self):
if self.firstname and self.lastname:
return self.firstname + " " + self.lastname
else:
return "no name"
class Phone(Model):
number = models.CharField(max_length=50, blank=True, null=True, verbose_name=u"Numéro de téléphone")
representative = models.ForeignKey(Representative, related_name="direct_phones", verbose_name=u"Élu")
class Meta:
depth = 0
def __str__(self):
return self.number
class LoomioTopic(Model):
title = models.CharField(max_length=255, verbose_name=u"Titre du sujet")