├── CNAME ├── Procfile ├── requirements.txt ├── client ├── src │ ├── index.css │ ├── img │ │ ├── bob.jpg │ │ ├── dan.jpg │ │ ├── jair.jpg │ │ ├── logo.png │ │ ├── mari.jpg │ │ ├── vini.jpg │ │ ├── dilmae.gif │ │ ├── hadri.jpg │ │ ├── italo.jpg │ │ ├── bg-part1.jpg │ │ ├── bg-part2.jpg │ │ ├── bg-part3.jpg │ │ ├── bg-part4.jpg │ │ ├── img-left-part1.png │ │ ├── s1.svg │ │ ├── s2.svg │ │ ├── s3.svg │ │ ├── pac-man-ghost.svg │ │ ├── megaphone.svg │ │ └── hackfest.svg │ ├── components │ │ ├── layout │ │ │ ├── Landing.js │ │ │ ├── Footer.js │ │ │ └── Header.js │ │ ├── auxiliar │ │ │ ├── Contributor.js │ │ │ ├── ContributorsGallery.js │ │ │ └── ShowcaseButton.js │ │ └── visualizacoes │ │ │ ├── GraficoPontos.js │ │ │ ├── Proporcao.js │ │ │ ├── VotosVSInvestimentos.js │ │ │ ├── GraficoLinhas.js │ │ │ └── GraficoBarras.js │ ├── fonts │ │ ├── Roboto-Thin.ttf │ │ ├── Gobold-Regular.otf │ │ └── Gloss_And_Bloom.ttf │ ├── style │ │ ├── style.css │ │ └── lineChart.css │ ├── index.js │ ├── App.test.js │ ├── html │ │ └── index.html │ ├── diamonds.css │ ├── logo.svg │ ├── registerServiceWorker.js │ ├── App.css │ ├── jquerydiamonds.js │ └── App.js ├── public │ ├── favicon.ico │ ├── Gloss_And_Bloom.ttf │ ├── Gobold-Regular.otf │ ├── manifest.json │ ├── index.html │ └── libs │ │ ├── style.css │ │ ├── vis-network.min.css │ │ ├── popper.min.js │ │ └── react.min.js ├── deploy-instrcs.md ├── .gitignore └── package.json ├── .gitignore ├── install_python_requeriments.sh ├── run.py ├── server ├── utilities │ ├── utils.py │ └── args.py ├── router.py └── database.py ├── populate_database ├── populate.py └── populator.py ├── LICENSE ├── r_scripts ├── filter_canditados_desistentes.R ├── processa_eleitores.R ├── processa_receitas.R ├── reduce_candidates_data_and_bind.R └── processa_candidato.R └── README.md /CNAME: -------------------------------------------------------------------------------- 1 | naonascipraseraoutra.surge.sh 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn -b 0.0.0.0:$PORT run:app 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask-cors 3 | pymongo 4 | flask_pymongo 5 | argparse 6 | gunicorn 7 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /data/ 3 | *.Rhistory 4 | *.RData 5 | 6 | *.pyc 7 | *.csv 8 | *.csv# 9 | 10 | -------------------------------------------------------------------------------- /client/src/img/bob.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/bob.jpg -------------------------------------------------------------------------------- /client/src/img/dan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/dan.jpg -------------------------------------------------------------------------------- /client/src/img/jair.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/jair.jpg -------------------------------------------------------------------------------- /client/src/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/logo.png -------------------------------------------------------------------------------- /client/src/img/mari.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/mari.jpg -------------------------------------------------------------------------------- /client/src/img/vini.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/vini.jpg -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/img/dilmae.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/dilmae.gif -------------------------------------------------------------------------------- /client/src/img/hadri.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/hadri.jpg -------------------------------------------------------------------------------- /client/src/img/italo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/italo.jpg -------------------------------------------------------------------------------- /install_python_requeriments.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get install python-setuptools 2 | pip install flask pymongo flask_pymongo argparse -------------------------------------------------------------------------------- /client/src/img/bg-part1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/bg-part1.jpg -------------------------------------------------------------------------------- /client/src/img/bg-part2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/bg-part2.jpg -------------------------------------------------------------------------------- /client/src/img/bg-part3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/bg-part3.jpg -------------------------------------------------------------------------------- /client/src/img/bg-part4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/bg-part4.jpg -------------------------------------------------------------------------------- /client/src/components/layout/Landing.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default () => { 4 | return
; 5 | }; 6 | -------------------------------------------------------------------------------- /client/public/Gloss_And_Bloom.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/public/Gloss_And_Bloom.ttf -------------------------------------------------------------------------------- /client/public/Gobold-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/public/Gobold-Regular.otf -------------------------------------------------------------------------------- /client/src/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /client/src/img/img-left-part1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/img/img-left-part1.png -------------------------------------------------------------------------------- /client/src/fonts/Gobold-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/fonts/Gobold-Regular.otf -------------------------------------------------------------------------------- /client/src/fonts/Gloss_And_Bloom.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hereismari/nao-nasci-pra-ser-a-outra/HEAD/client/src/fonts/Gloss_And_Bloom.ttf -------------------------------------------------------------------------------- /client/deploy-instrcs.md: -------------------------------------------------------------------------------- 1 | For deploying the front-end client, you can run: 2 | 3 | ``` 4 | npm run build 5 | cd build 6 | surge ./ $(cat ../../CNAME) 7 | ``` 8 | -------------------------------------------------------------------------------- /client/src/components/layout/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default () => { 4 | return
Rodapé
; 5 | }; 6 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from server.router import app 2 | import os 3 | 4 | if __name__ == '__main__': 5 | PORT = int(os.environ['PORT']) 6 | app.run(debug=True, port=PORT) 7 | -------------------------------------------------------------------------------- /client/src/style/style.css: -------------------------------------------------------------------------------- 1 | .background-degrade { 2 | background-image: linear-gradient(to right, #c0392b, #8e44ad); 3 | } 4 | 5 | .row { 6 | display: flex; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/components/layout/Header.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | export default class Header extends Component { 4 | render() { 5 | return
; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /client/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /server/utilities/utils.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify 2 | from flask import request 3 | 4 | 5 | # Auxiliar 6 | def wrapper_result(output): 7 | return jsonify({'result' : output}) 8 | 9 | def mount_request(args): 10 | flask_args = {} 11 | for arg in args: 12 | flask_args[arg] = request.args.get(args[arg][0], default=args[arg][1], type=args[arg][2]) 13 | return flask_args 14 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | .idea/* 24 | -------------------------------------------------------------------------------- /server/utilities/args.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | CANDIDATOS = { 4 | 'ano_eleicao': ('ano_eleicao', 2016, int), 5 | 'sexo': ('sexo', None, str), 6 | 'sigla_partido': ('sigla_partido', None, str), 7 | 'sigla_uf': ('sigla_uf', None, str), 8 | 'nome_municipio': ('nome_municipio', None, str), 9 | 'desc_sit_cand_tot': ('desc_sit_cand_tot', None, str) 10 | } 11 | 12 | 13 | # nao utilizado 14 | ELEITORES = { 15 | 'sexo': ('sexo', None, str), 16 | 'uf': ('uf', None, str), 17 | 'nome_municipio': ('nome_municipio', None, str), 18 | } -------------------------------------------------------------------------------- /populate_database/populate.py: -------------------------------------------------------------------------------- 1 | from populator import Populator 2 | 3 | import os 4 | 5 | import argparse 6 | parser = argparse.ArgumentParser(description='Popular banco do Nao nasci pra ser a outra.') 7 | parser.add_argument('--caminho-dados', type=str, default='../data/') 8 | 9 | 10 | def main(args, files=['partidos', 'historico']): 11 | populator = Populator() 12 | for f in files: 13 | filename = os.path.join(args.caminho_dados, f + '.csv') 14 | populator.populate(f, filename) 15 | 16 | 17 | if __name__ == '__main__': 18 | args = parser.parse_args() 19 | main(args) -------------------------------------------------------------------------------- /client/src/components/auxiliar/Contributor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./../../App.css"; 3 | 4 | class Contributor extends Component { 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | render() { 10 | return ( 11 |
12 |
13 | 14 |
15 |

16 | {this.props.name} 17 |

18 |
19 | ); 20 | } 21 | } 22 | export default Contributor; -------------------------------------------------------------------------------- /client/src/style/lineChart.css: -------------------------------------------------------------------------------- 1 | .axis { 2 | font-size: 10px; 3 | } 4 | 5 | .axis line, 6 | .axis path { 7 | fill: none; 8 | stroke: white; 9 | stroke-width: 1px; 10 | shape-rendering: crispEdges; 11 | } 12 | 13 | .axis text { 14 | fill: white; 15 | font-size: 10px; 16 | } 17 | 18 | .leftaxis > text, 19 | .rightaxis > text { 20 | font-size: 16px; 21 | } 22 | 23 | .rightaxis > text, 24 | .rightaxis .tick > text { 25 | stroke: none; 26 | fill: #ff9b27; 27 | } 28 | .rightaxis > path { 29 | stroke: #ff9b27; 30 | } 31 | 32 | .line-linechart { 33 | fill: none; 34 | stroke: white; 35 | stroke-width: 2px; 36 | } 37 | 38 | .line-linechart-rightaxis { 39 | fill: none; 40 | stroke: #ff9b27; 41 | stroke-width: 2px; 42 | } -------------------------------------------------------------------------------- /client/src/components/visualizacoes/GraficoPontos.js: -------------------------------------------------------------------------------- 1 | import { 2 | XYPlot, 3 | XAxis, 4 | YAxis, 5 | VerticalBarSeries, 6 | VerticalGridLines, 7 | HorizontalGridLines, 8 | MarkSeries 9 | } from "react-vis"; 10 | 11 | import React, { Component } from "react"; 12 | 13 | export default class GraficoPontos extends Component { 14 | render() { 15 | return ( 16 |
17 | 18 | 19 | 20 | 33 | 34 |
35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/img/s1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^1.5.0", 7 | "axios": "^0.18.0", 8 | "classnames": "^2.2.6", 9 | "d3": "^5.5.0", 10 | "d3-array": "^1.2.1", 11 | "d3-axis": "^1.0.8", 12 | "d3-brush": "^1.0.4", 13 | "d3-geo": "^1.10.0", 14 | "d3-scale": "^2.1.0", 15 | "d3-selection": "^1.3.0", 16 | "d3-shape": "^1.2.0", 17 | "d3-svg-legend": "^2.25.6", 18 | "d3-transition": "^1.1.1", 19 | "jquery": "^3.3.1", 20 | "react": "^16.4.2", 21 | "react-dom": "^16.4.2", 22 | "react-nvd3": "^0.5.7", 23 | "react-scripts": "1.1.4", 24 | "react-vis": "^1.10.4" 25 | }, 26 | "scripts": { 27 | "start": "react-scripts start", 28 | "build": "react-scripts build", 29 | "test": "react-scripts test --env=jsdom", 30 | "eject": "react-scripts eject", 31 | "deploy": "npm run build&&gh-pages -d build" 32 | }, 33 | "homepage": ".", 34 | "devDependencies": { 35 | "gh-pages": "^1.2.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Marianne Linhares Monteiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /client/src/img/s2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/src/img/s3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /r_scripts/filter_canditados_desistentes.R: -------------------------------------------------------------------------------- 1 | # ESSE SCRIPT TEM COMO OBJETIVO FILTRAR APENAS OS CANDIDATOS QUE RECEBERAM VERBAS DO PARTIDO. 2 | # PARA AQUELES QUE N RECEBERAM, INSERIMOS COMO 0. 3 | 4 | library(readr) 5 | library(tidyverse) 6 | 7 | receitas_por_candidato_merged_2016 <- read_csv("~/Área de Trabalho/hackfest-NNPSAO/nao-nasci-pra-ser-a-outra/data/investimentos_com_genero/receitas_por_candidato_merged_2016.csv") %>% 8 | select(-X1) 9 | 10 | receitas_por_candidatos_dadas_pelo_partido_merged_2016 <- read_csv("~/Área de Trabalho/hackfest-NNPSAO/nao-nasci-pra-ser-a-outra/data/investimentos_com_genero/receitas_por_candidatos_dadas_pelo_partido_merged_2016.csv") %>% select(-c(X1_1, X1)) 11 | 12 | dt_all = data.table::data.table(receitas_por_candidato_merged_2016) 13 | dt_part = data.table::data.table(receitas_por_candidatos_dadas_pelo_partido_merged_2016) 14 | 15 | candidatos_sem_verba_de_partido = anti_join(dt_all, dt_part, by="nome_candidato") 16 | candidatos_sem_verba_de_partido$verba_por_candidato = 0 17 | 18 | receitas_candidatos_dada_pelo_partido = rbind(candidatos_sem_verba_de_partido, 19 | as.data.frame(dt_part)) 20 | write.csv2(receitas_candidatos_dada_pelo_partido, "data/receitas_candidatos_dada_pelo_partido_2016") 21 | 22 | 23 | -------------------------------------------------------------------------------- /r_scripts/processa_eleitores.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | 3 | setwd("/home/italohmb/Área de Trabalho/hackfest-NNPSAO/nao-nasci-pra-ser-a-outra/") 4 | 5 | perfil_eleitorado_2012 = readr::read_csv2(here::here("data/Eleitores/perfil_eleitorado_2012/perfil_eleitorado_2012.csv"), local=readr::locale(encoding="latin1"), col_names = FALSE) 6 | perfil_eleitorado_2014 = readr::read_csv2(here::here("data/Eleitores/perfil_eleitorado_2014/perfil_eleitorado_2014.csv"), local=readr::locale(encoding="latin1"), col_names = FALSE) 7 | perfil_eleitorado_2016 = readr::read_csv2(here::here("data/Eleitores/perfil_eleitorado_2016/perfil_eleitorado_2016.csv"), local=readr::locale(encoding="latin1"), col_names = FALSE) 8 | perfil_eleitorado_ATUAL = readr::read_csv2(here::here("data/Eleitores/perfil_eleitorado_ATUAL/perfil_eleitorado_ATUAL.csv"), local=readr::locale(encoding="latin1"), col_names = FALSE) 9 | 10 | eleitores_all = rbind(perfil_eleitorado_2012, perfil_eleitorado_2014) 11 | eleitores_all = rbind(eleitores_all, perfil_eleitorado_2016) 12 | eleitores_all = rbind(eleitores_all, perfil_eleitorado_ATUAL) 13 | 14 | colnames(eleitores_all) = c("nome", "uf", "cidade", "x4", "x5", "genero", "faixa_etaria", "escolaridade", "idade") 15 | eleitores_all = eleitores_all %>% 16 | select(-c("x4", "x5")) 17 | 18 | write.csv2(eleitores_all, "data/preprocessed/eleitores_all.csv") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nao-nasci-pra-ser-a-outra 2 | 3 | ### [www.naonascipraseraoutra.com](http://www.naonascipraseraoutra.com) 4 | 5 | Monitoramento do cumprimento da Lei nº 9504/97 que diz que, "cada partido ou coligação deve preencher, nas eleições proporcionais, o mínimo de 30% e o máximo de 70% para candidaturas de cada sexo". 6 | 7 | Projeto desenvolvido no [4º Hackfest contra a corrupção](http://hackfest.com.br/). 8 | ![](http://www.jornaldaparaiba.com.br/app/uploads/2018/07/24-07-2018-hacfest.jpeg) 9 | 10 | ## Obtendo os dados 11 | 12 | Entre em contato com um dos desenvolvedores via email! 13 | 14 | ## Front End 15 | 16 | ReactJS 17 | 18 | 1. Para rodar o projeto React basta executar: 19 | 20 | ``` 21 | cd client 22 | npm install 23 | npm start 24 | ``` 25 | 26 | O cliente abrirá localmente em localhost:3000 27 | 28 | ## Back End 29 | 30 | Python 3.6. 31 | 32 | 1. [Instalar mongodb](http://www.bogotobogo.com/python/MongoDB_PyMongo/python_MongoDB_pyMongo_tutorial_installing.php) 33 | 2. Iniciar mongodb: `sudo service mongod start` 34 | 3. Instalar dependências: `chmod +x install_python_requeriments.sh; bash install_python_requeriments.sh` 35 | 4. Definir variável MONGODB_URI: `export MONGODB_URI='mongodb://localhost:27017/nao-nasci'` 36 | 5. Definir variável MONGODB_NAME no caso acima seria: `export MONGODB_NAME='nao_nasci'` 37 | 5. `PORT=5000 python run.py` 38 | 39 | 40 | ## Popular banco 41 | 42 | ```bash 43 | cd populate_database 44 | python populate --caminho-dados 45 | ``` 46 | -------------------------------------------------------------------------------- /client/src/components/auxiliar/ContributorsGallery.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from "react"; 2 | import "./../../App.css"; 3 | import Contributor from "./Contributor"; 4 | 5 | class ContributorsGallery extends Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | render() { 11 | return ( 12 |
13 | 15 | 17 | 19 | 21 | 23 | 25 | 27 |
28 | ); 29 | } 30 | } 31 | 32 | export default ContributorsGallery; -------------------------------------------------------------------------------- /client/src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 17 | 18 | Não nasci pra ser a outra 19 | 20 | 21 | 22 |
23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /client/src/components/auxiliar/ShowcaseButton.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 - 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | import React from 'react'; 22 | import PropTypes from 'prop-types'; 23 | 24 | class ShowcaseButton extends React.Component { 25 | render() { 26 | const {buttonContent, onClick} = this.props; 27 | return ( 28 | 33 | ); 34 | } 35 | } 36 | 37 | ShowcaseButton.PropTypes = { 38 | buttonContent: PropTypes.string.isRequired, 39 | onClick: PropTypes.func.isRequired 40 | }; 41 | 42 | export default ShowcaseButton; -------------------------------------------------------------------------------- /server/router.py: -------------------------------------------------------------------------------- 1 | # Dependencias 2 | from flask import Flask 3 | from flask_cors import CORS, cross_origin 4 | from flask import request 5 | 6 | from server.database import Client 7 | import server.utilities.utils as utils 8 | import server.utilities.args as args 9 | 10 | 11 | import os 12 | 13 | 14 | # App 15 | app = Flask(__name__) 16 | app.config['MONGO_URI'] = os.environ['MONGODB_URI'] 17 | app.config['CORS_HEADERS'] = 'Content-Type' 18 | 19 | cors = CORS(app) 20 | 21 | # Client MongoDB 22 | client = Client(app) 23 | 24 | # Rotas 25 | @app.route('/partidos/participacao/mulheres', methods=['GET']) 26 | @cross_origin() 27 | def partidos_participacao_mulheres(): 28 | _args = utils.mount_request(args.CANDIDATOS) 29 | return utils.jsonify(client.partidos_participacao_mulheres(_args)) 30 | 31 | 32 | @app.route('/partidos/ranking/zerovotos', methods=['GET']) 33 | @cross_origin() 34 | def partidos_ranking_zero_votos(): 35 | _args = utils.mount_request(args.CANDIDATOS) 36 | return utils.jsonify(client.partidos_ranking_zero_votos(_args)) 37 | 38 | 39 | @app.route('/partidos/media/zerovotos', methods=['GET']) 40 | @cross_origin() 41 | def partidos_media_zero_votos(): 42 | _args = utils.mount_request(args.CANDIDATOS) 43 | return utils.jsonify(client.partidos_media_zero_votos(_args)) 44 | 45 | 46 | @app.route('/historico', methods=['GET']) 47 | @cross_origin() 48 | def historico(): 49 | return utils.jsonify(client.historico()) 50 | 51 | 52 | 53 | 54 | @app.route('/favicon.ico', methods=['GET']) 55 | @cross_origin() 56 | def favicon(): 57 | return '' 58 | 59 | @app.route('/', methods=['GET']) 60 | @cross_origin() 61 | def index(): 62 | return 'hello world' 63 | 64 | 65 | @app.route('/home', methods=['GET']) 66 | @cross_origin() 67 | def home(): 68 | return '' 69 | -------------------------------------------------------------------------------- /client/src/img/pac-man-ghost.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /client/src/diamonds.css: -------------------------------------------------------------------------------- 1 | .diamonds { 2 | text-align: center; 3 | overflow: visible; 4 | white-space: nowrap; 5 | display: inline-block; 6 | } 7 | 8 | .diamond-row-wrap { 9 | text-align: center; 10 | position: relative; 11 | float: left; 12 | clear: both; 13 | } 14 | .diamond-row-upper, .diamond-row-lower { 15 | overflow: visible; 16 | clear: both; 17 | width: 100%; 18 | } 19 | .diamond-row-lower { 20 | position: absolute; 21 | bottom: 0; 22 | } 23 | .diamond-row-lower .diamond-box { 24 | margin-left: 64.644660941%; /* 0.5 + 1 / sqrt(2) / 2 */ 25 | margin-top: 64.644660941%; 26 | } 27 | 28 | .diamond-box-wrap { 29 | float: left; 30 | width: 250px; /* Size of diamonds */ 31 | height: 250px; 32 | } 33 | 34 | .diamond-box { 35 | -webkit-transform: rotate(45deg); 36 | -moz-transform: rotate(45deg); 37 | -ms-transform: rotate(45deg); 38 | transform: rotate(45deg); 39 | 40 | -webkit-box-sizing: border-box; 41 | -moz-box-sizing: border-box; 42 | -ms-box-sizing: border-box; 43 | box-sizing: border-box; 44 | 45 | overflow: hidden; 46 | 47 | position: relative; 48 | z-index: 1; 49 | 50 | width: 70.710678118%; /* x = sqrt(x^2 / 2) = 1 / sqrt(2) = 0.70710678118 */ 51 | height: 70.710678118%; 52 | margin: 14.644660941%; 53 | 54 | border: 1px solid transparent; 55 | } 56 | 57 | .diamond-box-inner { 58 | -webkit-transform: rotate(-45deg); 59 | -moz-transform: rotate(-45deg); 60 | -ms-transform: rotate(-45deg); 61 | transform: rotate(-45deg); 62 | 63 | -webkit-transform-origin: center center; 64 | -moz-transform-origin: center center; 65 | -ms-transform-origin: center center; 66 | transform-origin: center center; 67 | 68 | -webkit-box-sizing: border-box; 69 | -moz-box-sizing: border-box; 70 | -ms-box-sizing: border-box; 71 | box-sizing: border-box; 72 | 73 | width: 141.421356237%; /* sqrt(2) */ 74 | height: 141.421356237%; 75 | margin: -20.7106781185% 0 0 -20.7106781185%; /* (1 - sqrt(2)) / 2 */ 76 | } -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | Não nasci pra ser a outra 26 | 27 | 28 | 29 | 32 |
33 | 35 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /client/src/img/megaphone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /client/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /r_scripts/processa_receitas.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | library(data.table) 3 | 4 | receitas_candidatos_2016 <- readr::read_csv2(here::here("data/prestacao_brasil/receitas_candidatos_prestacao_contas_final_2016_brasil.csv"), 5 | local=readr::locale(encoding="latin1")) 6 | receitas_candidatos_2014 <- readr::read_csv2(here::here("data/prestacao_final_2014/receitas_candidatos_2014_brasil.csv"), 7 | local=readr::locale(encoding="latin1")) 8 | all_munzona_candidatos_data <- readr::read_csv2(here::here("all_munzona_candidatos_2016.csv"), 9 | local=readr::locale(encoding="latin1")) 10 | 11 | generos_2016 <- readr::read_csv(here::here("data/generos_processados/genero_2016.csv")) 12 | 13 | formata_colunas_receitas <- function(receitas) { 14 | to_underscore <- function(x) { 15 | gsub('([A-Za-z])([A-Z])([a-z])', '\\1_\\2\\3', x) %>% 16 | gsub('\\s+', '_', .) %>% 17 | gsub('.', '_', ., fixed = TRUE) %>% 18 | gsub('([a-z])([A-Z])', '\\1_\\2', .) %>% 19 | tolower() 20 | } 21 | 22 | new_names = names(receitas) %>% 23 | to_underscore() 24 | 25 | new_names 26 | } 27 | 28 | names(receitas_candidatos_2014) <- formata_colunas_receitas(receitas_candidatos_2014) 29 | names(receitas_candidatos_2016) <- formata_colunas_receitas(receitas_candidatos_2016) 30 | 31 | #Quanto cada candidato teve de receita 32 | receitas_por_candidatos <- 33 | receitas_candidatos_2016 %>% 34 | rename("numero_cand" = "numero_candidato") %>% 35 | dplyr::group_by(cpf_do_candidato, nome_candidato, numero_cand) %>% 36 | summarise(verba_por_candidato = sum(valor_receita)) 37 | 38 | #Quanto cada candidato teve de receita dada pelo partido 39 | receitas_por_candidatos_dadas_pelo_partido <- 40 | receitas_candidatos_2016 %>% 41 | filter(tipo_receita == "Recursos de partido político") %>% 42 | rename("numero_cand" = "numero_candidato") %>% 43 | dplyr::group_by(cpf_do_candidato, nome_candidato, numero_cand) %>% 44 | summarise(verba_por_candidato = sum(valor_receita)) 45 | 46 | receitas_por_candidatos <- data.table(receitas_por_candidatos) 47 | all_munzona_candidatos_data <- data.table(all_munzona_candidatos_data) 48 | receitas_por_candidatos_dadas_pelo_partido <- data.table(receitas_por_candidatos_dadas_pelo_partido) 49 | generos_2016 <- 50 | generos_2016 %>% 51 | select(-c(sigla_uf, des_situacao_candidatura)) %>% 52 | data.table() 53 | 54 | receitas_por_candidatos_merged <- 55 | merge(receitas_por_candidatos, all_munzona_candidatos_data, by = c("numero_cand", "nome_candidato")) 56 | receitas_por_candidatos_merged <- 57 | merge(receitas_por_candidatos_merged, generos_2016, by = c("numero_cand", "nome_municipio", "descricao_cargo")) 58 | receitas_por_candidatos_dadas_pelo_partido_merged <- 59 | merge(receitas_por_candidatos_dadas_pelo_partido, all_munzona_candidatos_data, by = c("numero_cand", "nome_candidato")) 60 | receitas_por_candidatos_dadas_pelo_partido_merged <- 61 | merge(receitas_por_candidatos_dadas_pelo_partido_merged, generos_2016, by = c("numero_cand", "nome_municipio", "descricao_cargo")) 62 | 63 | write_csv(receitas_por_candidatos_merged, "receitas_por_candidato_merged_2016.csv") 64 | write.csv(receitas_por_candidatos_dadas_pelo_partido_merged, "receitas_por_candidatos_dadas_pelo_partido_merged_2016.csv") 65 | -------------------------------------------------------------------------------- /populate_database/populator.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from pymongo import MongoClient 3 | 4 | import os 5 | 6 | 7 | class Populator(object): 8 | def __init__(self): 9 | self._client = MongoClient(os.environ['MONGODB_URI']) 10 | self._db = self._client[os.environ['MONGODB_NAME']] 11 | 12 | self._populate = { 13 | 'historico': self._default_populate, 14 | 'partidos': self._default_populate, 15 | } 16 | 17 | self._dict_collection = { 18 | 'historico': self._db.historico, 19 | 'partidos': self._db.partidos, 20 | } 21 | 22 | self._clean_up() 23 | 24 | 25 | def _clean_up(self): 26 | self._db.partidos.delete_many({}) 27 | self._db.historico.delete_many({}) 28 | 29 | 30 | def populate(self, file_type, filename): 31 | if file_type in self._populate: 32 | self._populate[file_type](filename, self._dict_collection[file_type]) 33 | else: 34 | raise Exception('Funcao para popular %s nao existente' % file_type) 35 | 36 | 37 | ''' A pressa eh amiga da perfeicao 38 | def _populate_partidos(self, filename): 39 | print('Popula partidos') 40 | data = [] 41 | with open(filename, encoding='utf-8') as csv_file: 42 | csv_reader = csv.DictReader(csv_file, delimiter=';') 43 | counter = 0 44 | for row in csv_reader: 45 | import ipdb; ipdb.set_trace() 46 | if row['num_turno'] != '1': 47 | continue 48 | 49 | if row['desc_sit_candidato'] in ['RENÚNCIA', 'CANCELAMENTO']: 50 | continue 51 | 52 | # construindo dado a ser salvo 53 | data_row = {} 54 | data_row['ano_eleicao'] = int(row['ano_eleicao']) 55 | data_row['sigla_uf'] = row['sigla_uf'] 56 | data_row['nome_municipio'] = row['nome_municipio'] 57 | data_row['eleito'] = 1 if row['desc_sit_cand_tot'] == 'ELEITO' else 0 58 | data_row['sigla_partido'] = row['sigla_partido'] 59 | 60 | if row['sexo'] == 'FEMININO': 61 | data_row['sexo'] = 1 62 | elif row['sexo'] == 'MASCULINO': 63 | data_row['sexo'] = 0 64 | else: 65 | data_row['sexo'] = -1 66 | 67 | # adicionando ao banco 68 | data.append(data_row) 69 | counter += 1 70 | if counter % 10000 == 0: 71 | print('Inserindo no banco %d...' % counter) 72 | self._db.candidatos.insert_many(data) 73 | data = [] 74 | ''' 75 | 76 | def _default_populate(self, filename, doc): 77 | data = [] 78 | with open(filename, encoding='utf-8') as csv_file: 79 | csv_reader = csv.DictReader(csv_file, delimiter=';') 80 | for row in csv_reader: 81 | data_row = {} 82 | for key in row: 83 | if key == '': 84 | continue 85 | try: 86 | data_row[key] = float(row[key]) 87 | except: 88 | data_row[key] = row[key] 89 | data.append(data_row) 90 | 91 | doc.insert_many(data) 92 | 93 | -------------------------------------------------------------------------------- /client/public/libs/style.css: -------------------------------------------------------------------------------- 1 | .react-vis-magic-css-import-rule{display:inherit}.rv-treemap{font-size:12px;position:relative}.rv-treemap__leaf{overflow:hidden;position:absolute}.rv-treemap__leaf--circle{align-items:center;border-radius:100%;display:flex;justify-content:center}.rv-treemap__leaf__content{overflow:hidden;padding:10px;text-overflow:ellipsis}.rv-xy-plot{color:#c3c3c3;position:relative}.rv-xy-plot canvas{pointer-events:none}.rv-xy-plot .rv-xy-canvas{pointer-events:none;position:absolute}.rv-xy-plot__inner{display:block}.rv-xy-plot__axis__line{fill:none;stroke-width:2px;stroke:#e6e6e9}.rv-xy-plot__axis__tick__line{stroke:#e6e6e9}.rv-xy-plot__axis__tick__text{fill:#6b6b76;font-size:11px}.rv-xy-plot__axis__title text{fill:#6b6b76;font-size:11px}.rv-xy-plot__grid-lines__line{stroke:#e6e6e9}.rv-xy-plot__circular-grid-lines__line{fill-opacity:0;stroke:#e6e6e9}.rv-xy-plot__series,.rv-xy-plot__series path{pointer-events:all}.rv-xy-plot__series--line{fill:none;stroke:#000;stroke-width:2px}.rv-crosshair{position:absolute;font-size:11px;pointer-events:none}.rv-crosshair__line{background:#47d3d9;width:1px}.rv-crosshair__inner{position:absolute;text-align:left;top:0}.rv-crosshair__inner__content{border-radius:4px;background:#3a3a48;color:#fff;font-size:12px;padding:7px 10px;box-shadow:0 2px 4px rgba(0,0,0,0.5)}.rv-crosshair__inner--left{right:4px}.rv-crosshair__inner--right{left:4px}.rv-crosshair__title{font-weight:bold;white-space:nowrap}.rv-crosshair__item{white-space:nowrap}.rv-hint{position:absolute;pointer-events:none}.rv-hint__content{border-radius:4px;padding:7px 10px;font-size:12px;background:#3a3a48;box-shadow:0 2px 4px rgba(0,0,0,0.5);color:#fff;text-align:left;white-space:nowrap}.rv-discrete-color-legend{box-sizing:border-box;overflow-y:auto;font-size:12px}.rv-discrete-color-legend.horizontal{white-space:nowrap}.rv-discrete-color-legend-item{color:#3a3a48;border-radius:1px;padding:9px 10px}.rv-discrete-color-legend-item.horizontal{display:inline-block}.rv-discrete-color-legend-item.horizontal .rv-discrete-color-legend-item__title{margin-left:0;display:block}.rv-discrete-color-legend-item__color{background:#dcdcdc;display:inline-block;height:2px;vertical-align:middle;width:14px}.rv-discrete-color-legend-item__title{margin-left:10px}.rv-discrete-color-legend-item.disabled{color:#b8b8b8}.rv-discrete-color-legend-item.clickable{cursor:pointer}.rv-discrete-color-legend-item.clickable:hover{background:#f9f9f9}.rv-search-wrapper{display:flex;flex-direction:column}.rv-search-wrapper__form{flex:0}.rv-search-wrapper__form__input{width:100%;color:#a6a6a5;border:1px solid #e5e5e4;padding:7px 10px;font-size:12px;box-sizing:border-box;border-radius:2px;margin:0 0 9px;outline:0}.rv-search-wrapper__contents{flex:1;overflow:auto}.rv-continuous-color-legend{font-size:12px}.rv-continuous-color-legend .rv-gradient{height:4px;border-radius:2px;margin-bottom:5px}.rv-continuous-size-legend{font-size:12px}.rv-continuous-size-legend .rv-bubbles{text-align:justify;overflow:hidden;margin-bottom:5px;width:100%}.rv-continuous-size-legend .rv-bubble{background:#d8d9dc;display:inline-block;vertical-align:bottom}.rv-continuous-size-legend .rv-spacer{display:inline-block;font-size:0;line-height:0;width:100%}.rv-legend-titles{height:16px;position:relative}.rv-legend-titles__left,.rv-legend-titles__right,.rv-legend-titles__center{position:absolute;white-space:nowrap;overflow:hidden}.rv-legend-titles__center{display:block;text-align:center;width:100%}.rv-legend-titles__right{right:0}.rv-radial-chart .rv-xy-plot__series--label{pointer-events:none} 2 | -------------------------------------------------------------------------------- /client/src/components/visualizacoes/Proporcao.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | import React from 'react'; 22 | 23 | import { 24 | XYPlot, 25 | XAxis, 26 | YAxis, 27 | VerticalGridLines, 28 | HorizontalGridLines, 29 | LineMarkSeriesCanvas, 30 | LineMarkSeries, 31 | LineSeriesCanvas, 32 | LineSeries, 33 | Crosshair 34 | } from 'react-vis'; 35 | 36 | function getRandomData() { 37 | return (new Array(1000)).fill(0).map((row, i) => ({ 38 | x: i, 39 | y: Math.random() * 20, 40 | color: Math.random() * 10 41 | })); 42 | } 43 | 44 | const randomData = getRandomData(); 45 | 46 | const colorRanges = { 47 | typeA: ['#59E4EC', '#0D676C'], 48 | typeB: ['#EFC1E3', '#B52F93'] 49 | }; 50 | 51 | const nextType = { 52 | typeA: 'typeB', 53 | typeB: 'typeA' 54 | }; 55 | 56 | const nextModeContent = { 57 | canvas: 'SWITCH TO SVG', 58 | svg: 'SWITCH TO CANVAS' 59 | }; 60 | 61 | const drawModes = ['canvas', 'svg']; 62 | 63 | export default class Example extends React.Component { 64 | state = { 65 | drawMode: 0, 66 | data: randomData, 67 | colorType: 'typeA', 68 | strokeWidth: 1, 69 | showMarks: true, 70 | value: false, 71 | hideComponent: false 72 | } 73 | 74 | render() { 75 | const { 76 | colorType, 77 | drawMode, 78 | data, 79 | hideComponent, 80 | strokeWidth, 81 | showMarks, 82 | value 83 | } = this.state; 84 | const lineSeriesProps = { 85 | animation: true, 86 | className: 'mark-series-example', 87 | sizeRange: [5, 15], 88 | color: colorType === 'typeA' ? '#0D676C' : '#B52F93', 89 | colorRange: colorRanges[colorType], 90 | opacityType: 'literal', 91 | strokeWidth, 92 | data, 93 | onNearestX: d => this.setState({value: d}) 94 | }; 95 | const SVGComponent = showMarks ? LineMarkSeries : LineSeries; 96 | const CanvasComponent = showMarks ? LineMarkSeriesCanvas : LineSeriesCanvas; 97 | 98 | const mode = drawModes[drawMode]; 99 | return ( 100 |
101 | 102 | {!hideComponent && this.setState({value: false})} 104 | width={600} 105 | height={300}> 106 | 107 | 108 | 109 | 110 | {mode === 'canvas' && 111 | } 112 | {mode === 'svg' && 113 | } 114 | {value && } 115 | } 116 |
117 | ); 118 | } 119 | } -------------------------------------------------------------------------------- /r_scripts/reduce_candidates_data_and_bind.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | library(stringr) 3 | 4 | filter_desistentes = function(df) { 5 | return( 6 | df %>% filter(!str_detect(desc_sit_candidato, regex("RENÚNCIA|CANCELAMENTO", ignore_case = FALSE))) 7 | ) 8 | } 9 | 10 | dell_cols = function(df) { 11 | return(df %>% 12 | select(-c(X1, 13 | nome_coligacao, 14 | nome_urna_candidato, 15 | composicao_legenda, 16 | nome_partido, 17 | descricao_cargo)) 18 | ) 19 | } 20 | 21 | filter_ghost_candidates = function(df) { 22 | df %>% 23 | filter(total_votos == 0) 24 | } 25 | 26 | filter_get_candidates_fem = function(df) { 27 | return( 28 | df %>% filter(str_detect(sexo, regex("FEMININO"))) 29 | ) 30 | } 31 | 32 | filter_get_candidates_masc = function(df) { 33 | return( 34 | df %>% filter(str_detect(sexo, regex("MASCULINO"))) 35 | ) 36 | } 37 | 38 | get_ghost_candidates = function(df) { 39 | df %>% mutate( 40 | total_candidate_fem = nrow(df %>% filter_get_candidates_fem), 41 | total_candidate_masc = nrow(df %>% filter_get_candidates_masc), 42 | total_ghosts_fem = nrow(df %>% filter_get_candidates_fem %>% filter_ghost_candidates), 43 | total_ghosts_masc = nrow(df %>% filter_get_candidates_masc %>% filter_ghost_candidates) 44 | ) 45 | } 46 | 47 | finish_clean = function(df) { 48 | return(df %>% 49 | select(ano_eleicao, 50 | total_candidate_fem, 51 | total_candidate_masc, 52 | total_ghosts_fem, 53 | total_ghosts_masc)) 54 | } 55 | 56 | preprocess = function(df) { 57 | return(df %>% 58 | filter_desistentes %>% 59 | dell_cols %>% 60 | get_ghost_candidates %>% 61 | finish_clean %>% 62 | head(1)) 63 | } 64 | 65 | data_2000 = readr::read_csv2(here::here("data/candidatos/candidatos_2000.csv"), local=readr::locale("br")) 66 | data_2002 = readr::read_csv2(here::here("data/candidatos/candidatos_2002.csv"), local=readr::locale("br")) 67 | data_2004 = readr::read_csv2(here::here("data/candidatos/candidatos_2004.csv"), local=readr::locale("br")) 68 | data_2006 = readr::read_csv2(here::here("data/candidatos/candidatos_2006.csv"), local=readr::locale("br")) 69 | data_2008 = readr::read_csv2(here::here("data/candidatos/candidatos_2008.csv"), local=readr::locale("br")) 70 | data_2010 = readr::read_csv2(here::here("data/candidatos/candidatos_2010.csv"), local=readr::locale("br")) 71 | data_2012 = readr::read_csv2(here::here("data/candidatos/candidatos_2012.csv"), local=readr::locale("br")) 72 | data_2014 = readr::read_csv2(here::here("data/candidatos/candidatos_2014.csv"), local=readr::locale("br")) 73 | data_2016 = readr::read_csv2(here::here("data/candidatos/candidatos_2016.csv"), local=readr::locale("br")) 74 | 75 | data_2000 = preprocess(data_2000) 76 | data_2002 = preprocess(data_2002) 77 | data_2004 = preprocess(data_2004) 78 | data_2006 = preprocess(data_2006) 79 | data_2008 = preprocess(data_2008) 80 | data_2010 = preprocess(data_2010) 81 | data_2012 = preprocess(data_2012) 82 | data_2014 = preprocess(data_2014) 83 | data_2016 = preprocess(data_2016) 84 | 85 | summarize_total_ghost_candidates = rbind(data_2000, data_2002) 86 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2004) 87 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2006) 88 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2008) 89 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2010) 90 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2012) 91 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2014) 92 | summarize_total_ghost_candidates = rbind(summarize_total_ghost_candidates, data_2016) 93 | 94 | summarize_total_ghost_candidates = summarize_total_ghost_candidates %>% 95 | mutate( 96 | porc_ghost_fem = round( 97 | total_ghosts_fem * 100 / total_candidate_fem, 98 | 3), 99 | porc_ghost_masc = round( 100 | total_ghosts_masc * 100 / total_candidate_masc, 101 | 3) 102 | ) 103 | 104 | write.csv2(summarize_total_ghost_candidates, "data/summarize_total_ghost_candidates.csv") 105 | -------------------------------------------------------------------------------- /client/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /server/database.py: -------------------------------------------------------------------------------- 1 | from pymongo import MongoClient 2 | from flask_pymongo import PyMongo 3 | 4 | 5 | class Client(object): 6 | '''Wrapper para operacoes com banco de dados.''' 7 | def __init__(self, app): 8 | self.client = PyMongo(app) 9 | 10 | 11 | def _update_query(self, query, args): 12 | for arg in args: 13 | if args[arg] is not None: 14 | query[arg] = args[arg] 15 | return query 16 | 17 | 18 | def historico(self): 19 | return [data for data in self.client.db.historico.find({}, {'_id': False})] 20 | 21 | 22 | def mulheres_eleitoras_vs_eleitas(self, args, ano): 23 | match = { 24 | 'genero': 'FEMININO', 25 | 'ano': ano 26 | } 27 | match = self._update_query({}, args) 28 | query = [{ 29 | '$match': match 30 | }, 31 | { 32 | '$group': { 33 | '_id': {}, 34 | 'eleitoras_mulheres': { 35 | '$sum': '$idade' 36 | } 37 | } 38 | } 39 | ] 40 | 41 | return [data for data in self.client.db.eleitores.aggregate(query)] 42 | 43 | 44 | def partidos_ranking_zero_votos(self, args): 45 | match = self._update_query({}, args) 46 | query = [{ 47 | '$match': match 48 | }, 49 | { 50 | '$group': { 51 | '_id': { 52 | 'sigla_partido': '$sigla_partido' 53 | }, 54 | 'total_mulheres': { 55 | '$sum': '$total_candidatas' 56 | }, 57 | 'total_mulheres_zero': { 58 | '$sum': '$cont_candidatas_zero_voto' 59 | } 60 | } 61 | }, 62 | { 63 | '$project': { 64 | '_id': '$_id', 65 | 'porcent_zero': { 66 | '$divide': ['$total_mulheres_zero', '$total_mulheres'] 67 | }, 68 | 'total_mulheres': '$total_mulheres', 69 | 'total_mulheres_zero': '$total_mulheres_zero' 70 | } 71 | }, 72 | { 73 | '$sort': { 74 | 'porcent_zero': -1 75 | } 76 | }, 77 | { 78 | '$limit': 3 79 | } 80 | ] 81 | 82 | return [data for data in self.client.db.partidos.aggregate(query)] 83 | 84 | 85 | def partidos_media_zero_votos(self, args): 86 | match = self._update_query({}, args) 87 | query = [{ 88 | '$match': match 89 | }, 90 | { 91 | '$group': { 92 | '_id': { 93 | 'sigla_partido': '$sigla_partido' 94 | }, 95 | 'total': { 96 | '$sum': '$cont_candidatas_zero_voto' 97 | } 98 | } 99 | }, 100 | { 101 | '$group': { 102 | '_id': {}, 103 | 'total': { 104 | '$avg': '$total' 105 | } 106 | } 107 | } 108 | ] 109 | 110 | return [data for data in self.client.db.partidos.aggregate(query)] 111 | 112 | 113 | def partidos_participacao_mulheres(self, args): 114 | match = self._update_query({}, args) 115 | query = [ 116 | { 117 | '$match': match 118 | }, 119 | { 120 | '$group': { 121 | '_id': { 122 | 'sigla_partido': '$sigla_partido' 123 | }, 124 | 'total': { 125 | '$sum': '$total_candidatos' 126 | }, 127 | 'total_mulheres': { 128 | '$sum': '$total_candidatas' 129 | } 130 | } 131 | }, 132 | { 133 | '$project': { 134 | 'porcentagem_mulheres': { 135 | '$divide': ['$total_mulheres', '$total'] 136 | }, 137 | 'total': '$total', 138 | 'total_mulheres': '$total_mulheres' 139 | } 140 | }, 141 | { 142 | '$sort': { 143 | 'porcentagem_mulheres': -1 144 | } 145 | } 146 | ] 147 | 148 | return [data for data in self.client.db.partidos.aggregate(query)] -------------------------------------------------------------------------------- /client/src/components/visualizacoes/VotosVSInvestimentos.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 - 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | import React from 'react'; 22 | import PropTypes from 'prop-types'; 23 | import { 24 | XYPlot, 25 | XAxis, 26 | YAxis, 27 | MarkSeries, 28 | Hint, 29 | makeWidthFlexible 30 | } from 'react-vis'; 31 | 32 | 33 | const tipStyle = { 34 | display: 'flex', 35 | color: '#fff', 36 | background: '#000', 37 | alignItems: 'right', 38 | padding: '2px' 39 | }; 40 | 41 | const boxStyle = {height: '2px', width: '2px'}; 42 | 43 | 44 | const dados = [ 45 | {_id:{sigla_partido:"pt"}, 46 | investimento: 1, 47 | votos: 10, 48 | n_mulheres: 30 49 | }, 50 | {_id:{sigla_partido:"novo"}, 51 | investimento: 10, 52 | votos: 25, 53 | n_mulheres: 12 54 | }, 55 | {_id:{sigla_partido:"pmdb"}, 56 | investimento: 40, 57 | votos: 2, 58 | n_mulheres: 65 59 | }, 60 | {_id:{sigla_partido:"psol"}, 61 | investimento: 104, 62 | votos: 80, 63 | n_mulheres: 5 64 | } 65 | ] 66 | 67 | export default class VotosVSInvestimentos extends React.Component { 68 | state = { 69 | value: false, 70 | 71 | } 72 | render() { 73 | const { 74 | value 75 | } = this.state; 76 | 77 | const dataPlot = dados.map(elem => { 78 | return { 79 | x: elem.investimento, 80 | y: elem.votos, 81 | partido: elem._id.sigla_partido, 82 | size: elem.n_mulheres, 83 | color: Math.random() 84 | } 85 | }); 86 | 87 | const partidos = dataPlot.map(elem =>{ 88 | return elem.partido 89 | } 90 | ) 91 | const colors = dataPlot.map(elem =>{ 92 | return elem.color 93 | }) 94 | const bubblePlot = ({ width }) => 101 | 102 | 103 | console.log(e)} 110 | onValueMouseOver={v => this.setState({value: v.x && v.y ? v: false}) } 111 | onSeriesMouseOut={() => this.setState({value: false})} 112 | > 113 | 114 | 115 | {this.state.value ? 116 |
117 |
118 | Partido {this.state.value.partido} com investimento de 119 | {this.state.value.x} milhões 120 | e {this.state.value.y} mil votos em {this.state.value.size} 121 | mulheres do partido. 122 |
123 | : null} 124 | ; 125 | bubblePlot.propTypes = { 126 | width: PropTypes.number, 127 | measurements: PropTypes.array 128 | }; 129 | const FlexibleBubblePlot = makeWidthFlexible(bubblePlot); 130 | return ( 131 |
132 |
133 | 134 |
135 |
136 | ); 137 | } 138 | } -------------------------------------------------------------------------------- /client/src/components/visualizacoes/GraficoLinhas.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./../../style/lineChart.css"; 3 | import { select } from "d3-selection"; 4 | import { max, min } from "d3-array"; 5 | import { axisBottom } from "d3-axis"; 6 | import * as d3 from "d3"; 7 | import axios from "axios"; 8 | 9 | const margin = { top: 40, right: 10, bottom: 40, left: 10 }; 10 | const width = 600 - margin.left - margin.right; 11 | const height = 600 - margin.top - margin.bottom; 12 | 13 | const API_DADOS = "http://naoaoutra.herokuapp.com/historico"; 14 | 15 | class LineChart extends Component { 16 | constructor(props) { 17 | super(props); 18 | this.createLineChart = this.createLineChart.bind(this); 19 | this.state = { isLoading: false, dados: [] }; 20 | } 21 | 22 | componentDidMount() { 23 | this.setState({ isLoading: true }); 24 | axios 25 | .get(API_DADOS) 26 | .then(res => res.data) 27 | .then(data => { 28 | const presidentialElections = data.filter(function (d) { 29 | return (d.ano_eleicao - 2000) % 4 === 0; 30 | }); 31 | this.setState({ dados: presidentialElections }) 32 | }) 33 | .catch(err => console.log(err)); 34 | this.createLineChart(); 35 | } 36 | 37 | componentDidUpdate() { 38 | this.createLineChart(); 39 | } 40 | 41 | createLineChart() { 42 | const node = this.node; 43 | 44 | const chart = select(node) 45 | .attr("viewBox", "0 0 " + (width + margin.left) + " " + (height + margin.top + margin.bottom)) 46 | .attr("width", "90%"); 47 | 48 | const parseTime = d3.timeParse("%Y"); 49 | 50 | const x = d3.scaleTime().range([0, width]); 51 | const y = d3.scaleLinear().range([height, 0]); 52 | 53 | const valueline = d3 54 | .line() 55 | .x(function(d) { 56 | return x(d.ano_eleicao); 57 | }) 58 | .y(function(d) { 59 | return y(d.total_candidate_fem); 60 | }); 61 | 62 | const valueline2 = d3 63 | .line() 64 | .x(function(d) { 65 | return x(d.ano_eleicao); 66 | }) 67 | .y(function(d) { 68 | return y(d.total_ghosts_fem); 69 | }); 70 | 71 | function draw(mData) { 72 | const data = mData; 73 | 74 | data.forEach(function(d) { 75 | d.ano_eleicao = parseTime(d.ano_eleicao); 76 | d.total_candidate_fem = +d.total_candidate_fem; 77 | d.total_ghosts_fem = +d.total_ghosts_fem; 78 | }); 79 | 80 | // sort years ascending 81 | data.sort(function(a, b) { 82 | return a["Date"] - b["Date"]; 83 | }); 84 | 85 | const dataMax = d3.max(data, function(d) { 86 | return Math.max(d.total_candidate_fem, d.total_ghosts_fem); 87 | }); 88 | 89 | // Scale the range of the data 90 | x.domain( 91 | d3.extent(data, function(d) { 92 | return d.ano_eleicao; 93 | }) 94 | ); 95 | 96 | y.domain([ 97 | 0, 98 | dataMax, 99 | ]); 100 | 101 | const g = chart 102 | .append("g") 103 | .attr("transform", "translate(" + 30 + ", " + 30 + ")"); 104 | 105 | // Add the valueline path. 106 | g.append("path") 107 | .data([data]) 108 | .attr("class", "line-linechart") 109 | .attr("d", valueline); 110 | 111 | // Add the valueline path. 112 | g.append("path") 113 | .data([data]) 114 | .attr("class", "line-linechart-rightaxis") 115 | .attr("d", valueline2); 116 | 117 | const parsedYear2009 = parseTime("2009"); 118 | g.append("line") 119 | .attr("x1", x(parsedYear2009)) 120 | .attr("y1", y(0)) 121 | .attr("x2", x(parsedYear2009)) 122 | .attr("y2", y(dataMax)) 123 | .style("stroke-dasharray", "3, 3") 124 | .style("stroke-width", 2) 125 | .style("stroke", "#fff43c") 126 | .style("fill", "none"); 127 | 128 | const lawYearTextOpacity = !isNaN(x(parsedYear2009)) ? 1 : 0; 129 | g.append("text") 130 | .attr("transform", "rotate(270)") 131 | .attr("y", function(){ return x(parsedYear2009) + 15 }) 132 | .attr("x", function(){ return -(height / 2) }) 133 | .attr('text-anchor', 'end') 134 | .attr("fill", "#fff43c") 135 | .attr("fill-opacity", lawYearTextOpacity) 136 | .attr("font-size", "16px") 137 | .text("Publicação da lei"); 138 | 139 | g.append("g") 140 | .attr("class", "axis axis--x") 141 | .attr("transform", "translate(0, " + height + ")") 142 | .call(axisBottom(x).ticks(6)); 143 | 144 | g.append("g") 145 | .attr("class", "axis axis--y leftaxis") 146 | .call(d3.axisLeft(y).ticks(10).tickFormat(function(data) {return parseInt(data / 1000) + "k";})) 147 | .attr("fill", "#fff") 148 | .append("text") 149 | .attr("transform", "rotate(-90)") 150 | .attr("y", 6) 151 | .attr("dy", ".71em") 152 | .attr("text-anchor", "end") 153 | .text("Candidatas"); 154 | 155 | g.append("g") 156 | .attr("class", "axis axis--y rightaxis") 157 | .attr("transform", "translate( " + width + ", 0 )") 158 | .call(d3.axisRight(y).tickSize(0).tickFormat("")) 159 | .attr("fill", "#FF9B27") 160 | .append("text") 161 | .attr("transform", "rotate(270)") 162 | .attr("y", 10) 163 | .attr("x", -height + 160) 164 | .attr("dy", "0.71em") 165 | .attr("text-anchor", "end") 166 | .text("Candidatas fanstamas"); 167 | } 168 | 169 | draw(this.state.dados); 170 | } 171 | 172 | render() { 173 | return ( 174 | (this.node = node)} width={width} height={height} /> 175 | ); 176 | } 177 | } 178 | export default LineChart; 179 | -------------------------------------------------------------------------------- /r_scripts/processa_candidato.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | 3 | munzona_candidatos_data <- readr::read_csv2(here::here("data/votacao_candidato_munzona_2016/votacao_candidato_munzona_2016_PB.csv"), 4 | local=readr::locale(encoding="latin1"), 5 | col_names = FALSE) 6 | 7 | munzona_partidos_data <- readr::read_csv2(here::here("data/votacao_partido_munzona_2016/votacao_partido_munzona_2016_PB.csv"), 8 | local=readr::locale(encoding="latin1"), 9 | col_names = FALSE) 10 | 11 | perfil_eleitorado_2016 <- readr::read_csv2(here::here("data/perfil_eleitorado_2016/perfil_eleitorado_2016.csv"), 12 | local=readr::locale(encoding="latin1"), 13 | col_names = FALSE) 14 | 15 | all_munzona_candidatos_data <- readr::read_csv2(here::here("data/votacao_candidato_munzona_2016/merged.csv"), 16 | local=readr::locale(encoding="latin1"), 17 | col_names = FALSE) 18 | all_munzona_partidos_data <- 19 | readr::read_csv2(here::here("data/votacao_partido_munzona_2016/merged.csv"), 20 | local=readr::locale(encoding="latin1"), 21 | col_names = FALSE) 22 | 23 | formata_candidatos_munzona <- function(ano = 2016, data) { 24 | col_names_candidatos <- tolower(c("DATA_GERACAO", "HORA_GERACAO","ANO_ELEICAO", "NUM_TURNO", 25 | "DESCRICAO_ELEICAO", "SIGLA_UF", "SIGLA_UE", "CODIGO_MUNICIPIO", 26 | "NOME_MUNICIPIO", "NUMERO_ZONA", "CODIGO_CARGO", "NUMERO_CAND", 27 | "SQ_CANDIDATO", "NOME_CANDIDATO", "NOME_URNA_CANDIDATO", "DESCRICAO_CARGO", 28 | "COD_SIT_CAND_SUPERIOR", "DESC_SIT_CAND_SUPERIOR", "CODIGO_SIT_CANDIDATO", "DESC_SIT_CANDIDATO", 29 | "CODIGO_SIT_CAND_TOT", "DESC_SIT_CAND_TOT", "NUMERO_PARTIDO", "SIGLA_PARTIDO", 30 | "NOME_PARTIDO", "SEQUENCIAL_LEGENDA", "NOME_COLIGACAO", "COMPOSICAO_LEGENDA", 31 | "TOTAL_VOTOS", "TRANSITO")) 32 | 33 | names(data) <- col_names_candidatos 34 | 35 | data %>% 36 | dplyr::group_by(sq_candidato, ano_eleicao, num_turno, sigla_uf, nome_municipio, nome_candidato, nome_urna_candidato, descricao_cargo, numero_cand, 37 | desc_sit_cand_superior, desc_sit_candidato, desc_sit_cand_tot, sigla_partido, nome_coligacao, composicao_legenda, 38 | transito, numero_cand) %>% 39 | summarise(total_votos = sum(total_votos)) 40 | } 41 | formata_partidos_munzona <- function(ano = 2016, data) { 42 | col_names_partidos <- tolower(c("DATA_GERACAO", "HORA_GERACAO", "ANO_ELEICAO", "NUM_TURNO", "DESCRICAO_ELEICAO", 43 | "SIGLA_UF", "SIGLA_UE", "CODIGO_MUNICIPIO", "NOME_MUNICIPIO", "NUMERO_ZONA", 44 | "CODIGO_CARGO", "DESCRICAO_CARGO", "TIPO_LEGENDA", "NOME_COLIGACAO", 45 | "COMPOSICAO_LEGENDA", "SIGLA_PARTIDO", "NUMERO_PARTIDO", "NOME_PARTIDO", 46 | "QTDE_VOTOS_NOMINAIS", "QTDE_VOTOS_LEGENDA", "TRANSITO", "SEQUENCIAL_COLIGACAO")) 47 | 48 | names(data) <- col_names_partidos 49 | 50 | data %>% 51 | dplyr::select(ano_eleicao, num_turno, sigla_uf, nome_municipio, descricao_cargo, nome_coligacao, composicao_legenda, sigla_partido, 52 | numero_partido, qtde_votos_nominais, qtde_votos_legenda, transito) 53 | } 54 | formata_eleitorado <- function(ano = 2016, data) { 55 | col_names_eleitorado <- tolower(c("PERIODO", "UF", "MUNICIPIO", "COD_MUNICIPIO_TSE", "NR_ZONA", "SEXO", 56 | "FAIXA_ETARIA", "GRAU_DE_ESCOLARIDADE", "QTD_ELEITORES_NO_PERFIL")) 57 | 58 | names(data) <- col_names_eleitorado 59 | 60 | data %>% 61 | dplyr::select(-c(cod_municipio_tse, nr_zona)) 62 | } 63 | 64 | all_munzona_candidatos_data <- formata_candidatos_munzona(2016, all_munzona_candidatos_data) 65 | all_munzona_partidos_data <- formata_partidos_munzona(2016, all_munzona_partidos_data) 66 | perfil_eleitorado_2016 <-formata_eleitorado(2016, perfil_eleitorado_2016) 67 | 68 | 69 | # Importa dados 70 | candidatos_data <- readr::read_csv2("data/consulta_cand_2016/merged.csv", 71 | local=locale(encoding = "latin1")) 72 | 73 | names(candidatos_data) = c("data_geracao", "hora_geracao", "ano_eleicao", 74 | "num_turno", "descricao_eleicao", "sigla_uf", 75 | "cod_cidade", "nome_municipio", "cod_cargo", "descricao_cargo", 76 | "nome_candidato", 77 | "sequencial_candidato", "numero_cand","cpf_candidato", 78 | "nome_urna_candidato","cod_situacao_candidatura", 79 | "desc_sit_cand_superior", "numero_partido", "sigla_partido", 80 | "nome_partido", "codigo_legenda","sigla_legenda", 81 | "composicao_legenda", "nome_coligacao", "codigo_ocupacao", 82 | "descricao_ocupacao", "data_nascimento", 83 | "num_tit_eleitoral_candidato", 84 | "idade_data_candidato", "cod_sexo", "sexo", 85 | "cod_grau_instrucao", 86 | "grau_instrucao", "cod_estado_civil", "estado_civil", 87 | "cod_cor_raca", "cor_raca", "codigo_nacionalidade", 88 | "descricao_nacionalidade", "sigla_UF_nascimento", 89 | "cod_municipio_nascimento", "nome_municipio_nascimento", 90 | "despesa_max_campanha", "cod_sit_tot_turno", 91 | "desc_sit_cand_tot", "email") 92 | 93 | sexo_raca_df <- candidatos_data %>% dplyr::select(numero_cand, sexo, cor_raca, nome_municipio, sigla_uf, descricao_cargo) 94 | 95 | data <- left_join(all_munzona_candidatos_data, sexo_raca_df, by=c("numero_cand", "nome_municipio", "descricao_cargo", "sigla_uf")) 96 | 97 | write.csv2(data, "data/candidatos_2016.csv") 98 | write.csv2(all_munzona_candidatos_data, "data/all_munzona_candidatos_2016.csv") 99 | -------------------------------------------------------------------------------- /client/src/img/hackfest.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/visualizacoes/GraficoBarras.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import { 4 | XYPlot, 5 | XAxis, 6 | YAxis, 7 | VerticalBarSeries, 8 | VerticalGridLines, 9 | HorizontalGridLines, 10 | MarkSeries, 11 | Hint, 12 | DecorativeAxis, 13 | makeWidthFlexible 14 | } from "react-vis"; 15 | 16 | import PropTypes from "prop-types"; 17 | 18 | import red from "@material-ui/core/colors/red"; 19 | import pink from "@material-ui/core/colors/pink"; 20 | import blue from "@material-ui/core/colors/blue"; 21 | import yellow from "@material-ui/core/colors/yellow"; 22 | import purple from "@material-ui/core/colors/purple"; 23 | import lightBlue from "@material-ui/core/colors/lightBlue"; 24 | import xAxis from "react-vis/dist/plot/axis/x-axis"; 25 | import axios from "axios"; 26 | import classnames from "classnames"; 27 | 28 | const getColorGrafico = porcMulheresPartido => { 29 | return parseInt(porcMulheresPartido * 10) * 100; 30 | }; 31 | 32 | const MARGIN = { 33 | left: 10, 34 | right: 10, 35 | bottom: 80, 36 | top: 60 37 | }; 38 | 39 | const API_DADOS_2014 = 40 | "http://naoaoutra.herokuapp.com/partidos/participacao/mulheres?ano_eleicao=2014"; 41 | 42 | const API_DADOS_2016 = 43 | "http://naoaoutra.herokuapp.com/partidos/participacao/mulheres?ano_eleicao=2016"; 44 | 45 | const tipStyle = { 46 | display: "flex", 47 | color: "#fff", 48 | background: "#000", 49 | alignItems: "right", 50 | padding: "2px" 51 | }; 52 | 53 | const boxStyle = { height: "2px", width: "2px" }; 54 | 55 | function buildValue(value) { 56 | return value; 57 | } 58 | 59 | export default class GraficoBarras extends Component { 60 | constructor(props) { 61 | super(props); 62 | this.state = { 63 | value: false, 64 | filterAno: "2016", 65 | dados2016: [], 66 | dados2014: [], 67 | isLoading: false, 68 | dados: [], 69 | width: 0, 70 | height: 0 71 | }; 72 | this.updateWindowDimensions = this.updateWindowDimensions.bind(this); 73 | } 74 | 75 | componentWillUnmount() { 76 | window.removeEventListener("resize", this.updateWindowDimensions); 77 | } 78 | 79 | updateWindowDimensions() { 80 | this.setState({ width: window.innerWidth, height: window.innerHeight }); 81 | } 82 | 83 | componentDidMount() { 84 | this.updateWindowDimensions(); 85 | window.addEventListener("resize", this.updateWindowDimensions); 86 | this.setState({ isLoading: true }); 87 | 88 | axios 89 | .get(API_DADOS_2014) 90 | .then(res => res.data) 91 | .then(data => this.setState({ dados2014: data })) 92 | .catch(err => console.log(err)); 93 | 94 | axios 95 | .get(API_DADOS_2016) 96 | .then(res => res.data) 97 | .then(data => 98 | this.setState({ dados: data, dados2016: data, isLoading: false }) 99 | ) 100 | .catch(err => console.log(err)); 101 | } 102 | 103 | filtraPorAno(e) { 104 | e.preventDefault(); 105 | if (e.target.id === "2014") { 106 | this.setState({ dados: this.state.dados2014 }); 107 | } else if (e.target.id === "2016") { 108 | this.setState({ dados: this.state.dados2016 }); 109 | } 110 | this.setState({ filterAno: e.target.id }); 111 | } 112 | 113 | // shouldComponentUpdate(nextProps, nextState) { 114 | // return ( 115 | // nextState.filterAno !== this.state.filterAno || 116 | // nextState.dados.length !== this.state.dados.length 117 | // ); 118 | // } 119 | 120 | render() { 121 | const maisMulheres = []; 122 | const menosMulheres = []; 123 | 124 | if (this.state.isLoading || this.state.dados.length === 0) { 125 | return
Loading......
; 126 | } 127 | 128 | const partidos = this.state.dados.map(elem => elem._id.sigla_partido); 129 | 130 | this.state.dados.map(elem => { 131 | if (elem.porcentagem_mulheres < 0.5) { 132 | menosMulheres.push({ 133 | x: elem._id.sigla_partido, 134 | y: -(1 - elem.porcentagem_mulheres), 135 | color: blue[getColorGrafico(1 - elem.porcentagem_mulheres)], 136 | legenda: 137 | "Porcentagem de homens: " + 138 | Math.round((1 - elem.porcentagem_mulheres) * 100) + 139 | "%" 140 | }); 141 | } else 142 | maisMulheres.push({ 143 | x: elem._id.sigla_partido, 144 | y: -elem.porcentagem_mulheres, 145 | color: pink[getColorGrafico(elem.porcentagem_mulheres)], 146 | legenda: 147 | "Porcentagem de mulheres: " + 148 | Math.round(elem.porcentagem_mulheres * 100) + 149 | "%" 150 | }); 151 | }); 152 | 153 | menosMulheres.sort((a, b) => { 154 | if (Math.abs(a.y) > Math.abs(b.y)) return 1; 155 | else if (Math.abs(a.y) < Math.abs(b.y)) return -1; 156 | else return 0; 157 | }); 158 | 159 | maisMulheres.sort((a, b) => { 160 | if (Math.abs(a.y) > Math.abs(b.y)) return -1; 161 | else if (Math.abs(a.y) < Math.abs(b.y)) return 1; 162 | else return 0; 163 | }); 164 | 165 | const defineTamFonte = () => { 166 | if (this.state.width < 500) return 6; 167 | else if (this.state.width < 645) return 11; 168 | else return 14; 169 | }; 170 | 171 | var finalData = []; 172 | 173 | maisMulheres.map(elem => { 174 | finalData.push(elem); 175 | }); 176 | menosMulheres.map(elem => { 177 | finalData.push(elem); 178 | }); 179 | 180 | const barChart = ({ width }) => ( 181 | 182 | 198 | 204 | this.setState({ value: v.x && v.y ? v : false }) 205 | } 206 | onSeriesMouseOut={() => this.setState({ value: false })} 207 | /> 208 | {this.state.value ? ( 209 | 210 |
211 |
212 | {this.state.value.legenda} 213 |
214 | 215 | ) : null} 216 | 217 | ); 218 | barChart.propTypes = { 219 | width: PropTypes.number, 220 | measurements: PropTypes.array 221 | }; 222 | 223 | const FlexibleBarChart = makeWidthFlexible(barChart); 224 | 225 | const filter = this.state.filterAno === "2014"; 226 | 227 | return ( 228 |
229 |
230 |
231 |
239 | 2014 240 |
241 |
247 | 2016 248 |
249 |
250 |
251 |
252 |
253 | % Mulheres 254 |
255 |
256 | % Homens 257 |
258 |
259 |
260 |
261 | . 262 |
263 | 264 |
265 |
266 | ); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "GoBold"; 3 | src: local("GoBold"), url(./fonts/Gobold-Regular.otf) format("truetype"); 4 | } 5 | 6 | @font-face { 7 | font-family: "Gloss And Bloom"; 8 | src: local("Gloss And Bloom"), 9 | url(./fonts/Gloss_And_Bloom.ttf) format("truetype"); 10 | } 11 | 12 | @font-face { 13 | font-family: "Roboto Thin"; 14 | src: local("Roboto Thin"), url(./fonts/Roboto-Thin.ttf) format("truetype"); 15 | } 16 | 17 | .part1 { 18 | background-image: url(img/bg-part1.jpg); 19 | background-size: cover; 20 | } 21 | 22 | .part2 { 23 | background-image: url(img/bg-part2.jpg); 24 | background-size: cover; 25 | padding: 5%; 26 | } 27 | 28 | .part3 { 29 | background-image: url(img/bg-part3.jpg); 30 | box-shadow: inset 0px 1000px 1000px 0px rgba(0, 0, 0, 0.7); 31 | background-size: cover; 32 | padding: 5%; 33 | } 34 | 35 | .part4 { 36 | background-image: url(img/bg-part4.jpg); 37 | background-size: cover; 38 | padding: 5%; 39 | } 40 | 41 | .part5 { 42 | background-image: linear-gradient(to right, #3b0d34, #891233); 43 | } 44 | 45 | .part6 { 46 | padding-left: 10%; 47 | padding-right: 10%; 48 | padding-top: 2%; 49 | padding-bottom: 2%; 50 | background-color: #001020; 51 | } 52 | 53 | .bottom-padding { 54 | padding-bottom: 5%; 55 | } 56 | 57 | .partItalo { 58 | background-image: linear-gradient(to right, #340c32, #881132); 59 | box-shadow: inset 0px 0px 1000px 0px rgba(0, 0, 0, 1); 60 | background-size: cover; 61 | padding: 5%; 62 | } 63 | 64 | .termometro { 65 | margin-bottom: -20px; 66 | background-image: linear-gradient(to right, #bc1f3f, #008aac); 67 | color: #4a148c; 68 | } 69 | 70 | .texto-termometro { 71 | font-family: "Gobold"; 72 | color: white; 73 | } 74 | 75 | .right-container { 76 | padding-bottom: 50px; 77 | } 78 | 79 | .right-container img { 80 | width: 100%; 81 | padding-top: 20%; 82 | padding-right: 20%; 83 | padding-left: 20%; 84 | padding-bottom: 5%; 85 | } 86 | 87 | .right-container h3 { 88 | font-size: 18px; 89 | text-align: center; 90 | padding-right: 5%; 91 | padding-left: 5%; 92 | color: white; 93 | font-family: "GoBold"; 94 | } 95 | 96 | .right-container h1 { 97 | font-size: 30px; 98 | text-align: center; 99 | padding-right: 5%; 100 | padding-left: 5%; 101 | color: white; 102 | font-family: "GoBold"; 103 | } 104 | 105 | .left-container { 106 | padding: 20%; 107 | } 108 | 109 | .left-container img { 110 | width: 100%; 111 | box-shadow: 10px 10px 0px 0px #fff43c; 112 | } 113 | 114 | .left-container h3 { 115 | position: absolute; 116 | width: 20%; 117 | color: white; 118 | margin: 30px; 119 | padding-top: 15%; 120 | font-size: 1em; 121 | font-family: "GoBold"; 122 | } 123 | 124 | .left-container span { 125 | position: absolute; 126 | width: 20%; 127 | color: white; 128 | margin: 30px; 129 | font-size: 4em; 130 | padding-top: 1%; 131 | font-family: "Gloss and Bloom"; 132 | } 133 | 134 | .container-part2 { 135 | padding-top: 50px; 136 | } 137 | 138 | .part2 h3 { 139 | color: white; 140 | font-size: 20px; 141 | font-family: "GoBold"; 142 | } 143 | 144 | .part2 span { 145 | color: white; 146 | font-size: 100px; 147 | font-family: "Gloss and Bloom"; 148 | } 149 | 150 | .text1-part2 div { 151 | width: 50%; 152 | margin: auto; 153 | } 154 | 155 | .text2-part2 { 156 | display: flex; 157 | justify-content: center; 158 | align-items: center; 159 | padding: 50px; 160 | } 161 | 162 | .text-part3 { 163 | margin: auto; 164 | } 165 | 166 | .part3, 167 | .part6, 168 | .part4 h1 { 169 | font-family: "Gobold"; 170 | color: white; 171 | } 172 | 173 | .partItalo h1 { 174 | font-family: "Gobold"; 175 | color: white; 176 | } 177 | 178 | .part3 h1 { 179 | padding-bottom: 20px; 180 | } 181 | 182 | .part4 h1 { 183 | padding-bottom: 50px; 184 | } 185 | 186 | .part6 h1 { 187 | font-size: 20px; 188 | } 189 | 190 | .text-3-part3 div { 191 | width: 50%; 192 | margin: auto; 193 | } 194 | 195 | .text-part3, 196 | h3 { 197 | font-family: "Gobold"; 198 | color: white; 199 | font-size: 20px; 200 | } 201 | 202 | .text-part3, 203 | span { 204 | font-family: "Gloss and Bloom"; 205 | color: white; 206 | font-size: 2em; 207 | } 208 | 209 | .ajuste-porcent-sub { 210 | width: 40%; 211 | margin: auto; 212 | } 213 | 214 | .ajuste-porcent-sub h3 { 215 | font-size: 20px; 216 | } 217 | 218 | .card { 219 | margin: auto; 220 | padding: 10%; 221 | box-shadow: 10px 10px 0px 0px #fff43c; 222 | background-color: #430d34; 223 | } 224 | 225 | .rr-1 { 226 | margin-right: auto; 227 | text-align: center; 228 | justify-content: center; 229 | margin-left: auto; 230 | margin-top: 10%; 231 | margin-bottom: 10%; 232 | } 233 | 234 | .rr1-img-part { 235 | width: 20%; 236 | margin-left: 5%; 237 | padding-right: 5%; 238 | margin-top: auto; 239 | margin-bottom: auto; 240 | } 241 | 242 | .rr1-partido { 243 | width: 40%; 244 | margin-right: 5%; 245 | margin-top: auto; 246 | padding-left: 5%; 247 | margin-bottom: auto; 248 | -ms-flex-align: center; 249 | -ms-flex-pack: center; 250 | justify-content: center; 251 | } 252 | 253 | .nomepartido { 254 | -ms-flex-line-pack: center; 255 | align-content: center; 256 | font-size: 2em; 257 | margin-top: auto; 258 | margin-bottom: auto; 259 | font-family: "GoBold"; 260 | color: white; 261 | } 262 | 263 | .ttpartido { 264 | font-size: 1em; 265 | font-family: "GoBold"; 266 | color: #dee2e6; 267 | } 268 | 269 | .ttpartido i { 270 | margin-right: 10px; 271 | } 272 | 273 | .rr1-img-part img { 274 | width: 100%; 275 | } 276 | 277 | .ghost-size { 278 | width: 50%; 279 | padding: 10%; 280 | } 281 | 282 | .ghost-size img { 283 | width: 100%; 284 | } 285 | 286 | .nota { 287 | margin: auto; 288 | width: 50%; 289 | padding-right: 5%; 290 | } 291 | 292 | .nota h3 { 293 | margin: auto; 294 | font-size: 1em; 295 | text-align: left; 296 | } 297 | 298 | .denuncia { 299 | border-style: solid; 300 | border-width: 3px; 301 | border-color: white; 302 | padding: 5%; 303 | -webkit-transition: 0.1s; 304 | -o-transition: 0.1s; 305 | transition: 0.1s; 306 | } 307 | 308 | .denuncia:hover { 309 | background-color: white; 310 | } 311 | 312 | .denuncia:hover p { 313 | color: black; 314 | } 315 | 316 | .denuncia p { 317 | font-family: "GoBold"; 318 | margin: auto; 319 | color: white; 320 | font-size: 15px; 321 | } 322 | 323 | .denuncia a { 324 | font-family: "GoBold"; 325 | margin: auto; 326 | color: white; 327 | font-size: 15px; 328 | } 329 | 330 | .denuncia i { 331 | font-size: 20px; 332 | margin-left: 10%; 333 | } 334 | 335 | .twitter i { 336 | font-size: 40px; 337 | margin-left: 10%; 338 | } 339 | 340 | .botao { 341 | text-align: center; 342 | } 343 | 344 | .row-ranking { 345 | padding-left: 5%; 346 | padding-right: 5%; 347 | padding-bottom: 5%; 348 | } 349 | 350 | .ajust-margin { 351 | margin: auto; 352 | } 353 | 354 | .ajust-margin2 { 355 | margin-top: auto; 356 | margin-bottom: auto; 357 | } 358 | 359 | .colaborartext { 360 | text-align: center; 361 | margin: auto; 362 | } 363 | 364 | .colaborartext p { 365 | color: white; 366 | font-family: "GoBold"; 367 | font-size: 2em; 368 | margin-bottom: 5%; 369 | } 370 | 371 | .colaborartext img { 372 | width: 35%; 373 | } 374 | 375 | .megaphonediv { 376 | width: 30%; 377 | } 378 | 379 | .blink_me { 380 | animation: blinker 0.2s cubic-bezier(0.99, 0, 0, 1.01) infinite; 381 | } 382 | 383 | @keyframes blinker { 384 | 50% { 385 | color: #fff43c; 386 | } 387 | } 388 | 389 | .part5 h1 { 390 | font-family: "Gobold"; 391 | color: white; 392 | } 393 | 394 | .footer { 395 | background-color: #001020; 396 | padding-top: 10%; 397 | padding-bottom: 10%; 398 | } 399 | 400 | @media only screen and (min-width: 990px) { 401 | .contributor-image { 402 | padding-left: 15px; 403 | padding-right: 15px; 404 | } 405 | } 406 | 407 | .contributor-name { 408 | margin: 0; 409 | margin-top: 10px; 410 | padding: 0; 411 | text-align: center; 412 | font-size: 0.8em; 413 | font-weight: bold; 414 | } 415 | 416 | .ajust-img-pocs { 417 | width: 100%; 418 | border-radius: 50%; 419 | margin: auto; 420 | } 421 | 422 | .text-footer { 423 | font-family: "GoBold"; 424 | color: white; 425 | font-size: 8px; 426 | } 427 | 428 | .logo-footer { 429 | text-align: center; 430 | align-items: center; 431 | justify-content: center; 432 | margin: auto; 433 | margin-top: 5%; 434 | } 435 | 436 | .logo-hack { 437 | width: 25%; 438 | margin-right: 60px; 439 | } 440 | 441 | .denuncia:hover a { 442 | color: black; 443 | } 444 | .denuncia:hover i { 445 | color: black; 446 | } 447 | .twitter { 448 | -ms-flex-line-pack: center; 449 | align-content: center; 450 | -ms-flex-pack: center; 451 | justify-content: center; 452 | overflow: hidden; 453 | font-weight: 200; 454 | text-align: center; 455 | } 456 | .twitter a { 457 | color: white; 458 | font-family: "Gobold"; 459 | vertical-align: middle; 460 | font-size: 2rem; 461 | } 462 | .twitter-logo-size { 463 | width: 2.5rem; 464 | filter: none; 465 | margin-right: 4%; 466 | vertical-align: middle; 467 | transition: 0.1s; 468 | } 469 | 470 | .logo-nossa { 471 | width: 20%; 472 | margin-left: 60px; 473 | } 474 | -------------------------------------------------------------------------------- /client/src/jquerydiamonds.js: -------------------------------------------------------------------------------- 1 | (function($, window, document, undefined) { 2 | 'use strict'; 3 | 4 | var Diamonds = function(customOptions) { 5 | this.options = { 6 | itemSelector : ".item", 7 | size : 250, 8 | gap : 0.5, 9 | autoRedraw : true, 10 | hideIncompleteRow : false, 11 | scrollbarWidth : 0, 12 | minDiamondsPerRow : 2, 13 | eventPrefix : "diamonds:", 14 | itemWrap : $('
'), 15 | rowWrap : $('
'), 16 | rowUpperWrap : $('
'), 17 | rowLowerWrap : $('
'), 18 | diamondWrap : $('
'), 19 | overrideCss : '.diamonds-{{guid}} .diamond-box-wrap { width: {{size}}px; height: {{size}}px; } .diamonds-{{guid}} .diamond-box { border-width: {{gap}}px }', 20 | debugEnabled : false, 21 | debugEvent : function(event, data) { console.debug("Event: " + event, data); }, 22 | debugMethod : function(method, args) { console.debug("Method: " + method, args)} 23 | }; 24 | this.wrapElement = customOptions.wrapElement; 25 | 26 | if(this._triggerEvent("beforeInit")) return; 27 | 28 | this.setOptions(customOptions, false); 29 | 30 | this.itemElements = $(this.options.itemSelector, this.wrapElement); 31 | 32 | this.guid = this._createGuid(); 33 | 34 | // Create override css 35 | this.styleElement = this._createOverrideCss(); 36 | 37 | // Initial draw 38 | this.draw(); 39 | 40 | // Auto redraw 41 | this.startAutoRedraw(); 42 | 43 | this._triggerEvent("afterInit"); 44 | }; 45 | 46 | /** 47 | * Returns true if we should stop 48 | */ 49 | Diamonds.prototype._triggerEvent = function(event, data) { 50 | if(this.options.debugEnabled) this.options.debugEvent(event, data); 51 | var e = $.Event(this.options.eventPrefix + event); 52 | this.wrapElement.trigger(e, data); 53 | return e.isDefaultPrevented(); 54 | }; 55 | 56 | Diamonds.prototype._createGuid = function() { 57 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = Math.random()*16|0,v=c=='x'?r:r&0x3|0x8;return v.toString(16);}); 58 | }; 59 | 60 | Diamonds.prototype.destroy = function() { 61 | if(this._triggerEvent("beforeDestroy")) return; 62 | 63 | this._removeOverrideCss(); 64 | this.stopAutoRedraw(); 65 | this._emptyElement(this.wrapElement); 66 | this.wrapElement.append(this.itemElements); 67 | this.wrapElement.removeData("diamonds"); 68 | 69 | this._triggerEvent("afterDestroy"); 70 | }; 71 | 72 | Diamonds.prototype._createOverrideCss = function() { 73 | var css = this.options.overrideCss; 74 | var data = { 75 | "size" : this.options.size, 76 | "gap" : this.options.gap, 77 | "guid" : this.guid 78 | } 79 | for(var key in data) { 80 | if(data.hasOwnProperty(key)) { 81 | css = css.replace(new RegExp("{{" + key + "}}", 'g'), data[key]); 82 | } 83 | } 84 | 85 | var style = $(''); 86 | style.html(css); 87 | 88 | $("head").append(style); 89 | return style; 90 | }; 91 | 92 | Diamonds.prototype._removeOverrideCss = function() { 93 | if(this.styleElement) this.styleElement.remove(); 94 | }; 95 | 96 | Diamonds.prototype._updateOverrideCss = function() { 97 | this._removeOverrideCss(); 98 | this._createOverrideCss(); 99 | }; 100 | 101 | Diamonds.prototype.stopAutoRedraw = function() { 102 | if(this._triggerEvent("beforeStopAutoRedraw")) return; 103 | window.clearInterval(this.redrawer); 104 | this._triggerEvent("afterStopAutoRedraw"); 105 | }; 106 | 107 | Diamonds.prototype.startAutoRedraw = function() { 108 | window.clearInterval(this.redrawer); // Stop previous 109 | var lastWidth = this.wrapElement.width(); 110 | if(this.options.autoRedraw) { 111 | if(this._triggerEvent("beforeStartAutoRedraw")) return; 112 | 113 | this.redrawer = window.setInterval(function() { 114 | var curWidth = this.wrapElement.width(); 115 | if(curWidth !== lastWidth) { 116 | if(this._triggerEvent("onAutoResize", {before: lastWidth, current: curWidth})) return; 117 | lastWidth = curWidth; 118 | this.draw(); 119 | } 120 | }.bind(this), 50); 121 | 122 | this._triggerEvent("afterStartAutoRedraw"); 123 | } 124 | }; 125 | 126 | Diamonds.prototype.setOptions = function(customOptions, redraw) { 127 | redraw = redraw === undefined ? true : redraw; 128 | if(customOptions !== null && typeof customOptions === "object") { 129 | 130 | if(this._triggerEvent("beforeSetOptions", customOptions)) return; 131 | 132 | // Stop or start redraw 133 | if(this.options.autoRedraw && !customOptions.autoRedraw) { 134 | this.stopAutoRedraw(); 135 | } 136 | else if(!this.options.autoRedraw && customOptions.autoRedraw) { 137 | this.startAutoRedraw(); 138 | } 139 | 140 | $.extend(true, this.options, customOptions); 141 | 142 | if(redraw) { 143 | this._updateOverrideCss(); 144 | this.draw(); 145 | } 146 | 147 | this._triggerEvent("afterSetOptions", customOptions); 148 | } 149 | }; 150 | 151 | Diamonds.prototype._getScrollbarWidth = function() { 152 | if($.isNumeric(this.options.scrollbarWidth) && this.options.scrollbarWidth >= 0) { 153 | return this.options.scrollbarWidth; 154 | } 155 | 156 | var $inner = $('
test
'), 157 | $outer = $('
').append($inner), 158 | inner = $inner[0], 159 | outer = $outer[0]; 160 | 161 | $('body').append(outer); 162 | var width1 = inner.offsetWidth; 163 | $outer.css('overflow', 'scroll'); 164 | var width2 = outer.clientWidth; 165 | $outer.remove(); 166 | 167 | return this.scrollbarWidth = (width1 - width2); 168 | }; 169 | 170 | Diamonds.prototype._emptyElement = function(element) { 171 | $("> *", element).detach(); 172 | }; 173 | 174 | Diamonds.prototype._groupIntoRows = function(items, maxDiamondsPerRow, hideIncompleteRow) { 175 | // Max number of diamonds per row 176 | maxDiamondsPerRow = Math.max(this.options.minDiamondsPerRow, 0 + maxDiamondsPerRow); 177 | 178 | // Draw rows 179 | var rows = new Array(); 180 | for(var i = 0; i < items.length; i++) { 181 | var item = items[i]; 182 | // Append or create new row? 183 | var max = rows.length % 2 === 0 ? maxDiamondsPerRow - 1 : maxDiamondsPerRow; 184 | if(!rows.hasOwnProperty(rows.length - 1) || rows[rows.length - 1].length == max) { 185 | rows.push(new Array()); 186 | } 187 | rows[rows.length - 1].push(item); 188 | } 189 | 190 | // Hide incomplete rows 191 | if(hideIncompleteRow) { 192 | if(rows.hasOwnProperty(rows.length - 1) && rows[rows.length - 1].length < rows.length % 2 === 0 ? maxDiamondsPerRow - 1 : maxDiamondsPerRow) { 193 | rows.pop(); 194 | } 195 | } 196 | 197 | return rows; 198 | }; 199 | 200 | Diamonds.prototype._renderHtml = function(rows) { 201 | var wrap = this.options.diamondWrap.clone(); 202 | wrap.addClass("diamonds-" + this.guid); 203 | for(var i = 0; i < rows.length; i += 2) { 204 | var row = this.options.rowWrap.clone(); 205 | var upper = this.options.rowUpperWrap.clone(); 206 | var lower = this.options.rowLowerWrap.clone(); 207 | row.append(upper).append(lower); 208 | wrap.append(row); 209 | 210 | for(var j = 0; j < rows[i].length; j++) { 211 | upper.append(rows[i][j]); 212 | $(rows[i][j]).wrap(this.options.itemWrap); 213 | } 214 | if(!rows.hasOwnProperty(i + 1)) break; 215 | for(var j = 0; j < rows[i + 1].length; j++) { 216 | lower.append(rows[i + 1][j]); 217 | $(rows[i + 1][j]).wrap(this.options.itemWrap); 218 | } 219 | } 220 | 221 | wrap.css("min-width", this.options.minDiamondsPerRow * this.options.size); 222 | 223 | return wrap; 224 | }; 225 | 226 | Diamonds.prototype.draw = function() { 227 | if(this._triggerEvent("beforeDraw")) return; 228 | 229 | this._emptyElement(this.wrapElement); 230 | 231 | var width = this.options.wrapElement.width() - this._getScrollbarWidth(); 232 | 233 | var rows = this._groupIntoRows(this.itemElements, 234 | Math.floor(width / this.options.size), 235 | this.options.hideIncompleteRow); 236 | 237 | var html = this._renderHtml(rows); 238 | 239 | this.wrapElement.append(html); 240 | 241 | this._triggerEvent("afterDraw"); 242 | }; 243 | 244 | 245 | // jQuery stuff 246 | $.fn.diamonds = function(method) { 247 | 248 | // Initialize 249 | if(method === undefined || $.isPlainObject(method)) { 250 | method = method || {}; 251 | method.wrapElement = this; 252 | this.data("diamonds", new Diamonds(method)); 253 | return this; 254 | } 255 | 256 | // Call method 257 | var inst = this.data("diamonds"); 258 | if(inst == null) throw new Error("Diamonds not initialized on this element."); 259 | 260 | if(Diamonds.prototype.hasOwnProperty(method)) { 261 | var args = Array.prototype.slice.call(arguments); 262 | args.shift(); 263 | if(inst.options.debugEnabled) inst.options.debugMethod(method, args); 264 | var ret = Diamonds.prototype[method].apply(inst, args); 265 | return ret === undefined ? this : ret; 266 | } 267 | }; 268 | })(window.hasOwnProperty("Zepto") ? window.Zepto : window.jQuery, window, document); 269 | -------------------------------------------------------------------------------- /client/public/libs/vis-network.min.css: -------------------------------------------------------------------------------- 1 | .vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%)}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}div.vis-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none;z-index:5}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%)}div.vis-color-picker div.vis-new-color{position:absolute;width:140px;height:20px;border:1px solid rgba(0,0,0,.1);border-radius:5px;top:380px;left:159px;text-align:right;padding-right:2px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-color-picker div.vis-initial-color{position:absolute;width:140px;height:20px;border:1px solid rgba(0,0,0,.1);border-radius:5px;top:380px;left:10px;text-align:left;padding-left:2px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px}div.vis-network div.vis-manipulation{box-sizing:content-box;border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;cursor:pointer;padding:0 8px 0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px} -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import "./App.css"; 3 | import GraficoBarras from "./components/visualizacoes/GraficoBarras"; 4 | import LineChart from "./components/visualizacoes/GraficoLinhas"; 5 | import axios from "axios"; 6 | import ContributorsGallery from "./components/auxiliar/ContributorsGallery"; 7 | 8 | class App extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { dadosRanking: [], isLoading: false }; 12 | } 13 | componentDidMount() { 14 | this.setState({ isLoading: true }); 15 | axios 16 | .get("http://naoaoutra.herokuapp.com/partidos/ranking/zerovotos") 17 | .then(res => res.data) 18 | .then(data => this.setState({ dadosRanking: data, isLoading: false })); 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 |
25 |
26 |
27 |
28 |
29 | 10,5% 30 |

Apenas 10,5% do congresso é feminino.

31 |

32 | 33 |

34 |
35 |
36 | 37 |

38 | O Não Nasci Pra Ser a Outra oferece uma maneira fácil de 39 | monitorar o cumprimento efetivo da{" "} 40 | 41 | lei 12.034/2009,{" "} 42 | {" "} 43 | a qual exige que haja no mínimo 30% e o máximo 70% candidatos de 44 | cada sexo por partido ou coligação. 45 |

46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 |
56 | 30% 57 |

58 | É o mínimo de candidatos de um gênero que um partido ou 59 | coligação deve ter. 60 |

61 |
62 |
63 |
64 |

65 | A lei 12.034/2009 tem como um dos interesses aumentar a 66 | proporção de mulheres dentro da política. Acima, podemos ver 67 | como os partidos estão agindo com a representatividade 68 | feminina nas candidaturas de eleições. O tom roseado indica 69 | os partidos onde as mulheres são maioria. Em um universo 70 | ideal os partidos deveriam ter 50% de homens e 50% de 71 | mulheres. 72 |

73 |
74 |
75 |
76 |
77 |
78 |

79 | Evolução do número de candidatas 80 |

81 |
82 | 83 |
84 |
85 |
86 |

87 | Para entender o impacto de uma lei na representatividade 88 | feminina na política precisamos também entender que nem tudo 89 | se resolve com proposições e porcentagens. Ainda há no Brasil 90 | candidatas fantasmas, aquelas que não receberam voto algum. Em 91 | geral, elas são colocadas para preencher a cota mas sem 92 | nenhuma intenção de se elegerem. 93 |

94 |
95 |
96 |
97 | {this.state.isLoading || this.state.dadosRanking.length === 0 ? ( 98 |
Carregando.......
99 | ) : ( 100 |
101 |

102 | Raking dos partidos
103 | com maior proporção de prováveis candidatas fantasmas em 2016 104 |

105 |
106 |
107 |
108 |
109 |
110 | 111 |
112 |
113 |

114 | {this.state.dadosRanking[0]._id.sigla_partido} 115 |

116 |
117 |
118 |
119 |
120 |
121 | 122 |
123 |
124 |

125 | Proporção de mulheres fantasmas:{" "} 126 | {this.state.dadosRanking[0].porcent_zero * 100} % 127 |

128 |
129 |
130 | 142 |
143 |
144 | 145 |
146 |
147 |
148 | 149 |
150 |
151 |

152 | {this.state.dadosRanking[1]._id.sigla_partido} 153 |

154 |
155 |
156 |
157 |
158 |
159 | 160 |
161 |
162 |

163 | Proporção de mulheres fantasmas:{" "} 164 | {this.state.dadosRanking[1].porcent_zero * 100} % 165 |

166 |
167 |
168 | 180 |
181 |
182 |
183 |
184 |
185 | 186 |
187 |
188 |

189 | {this.state.dadosRanking[2]._id.sigla_partido} 190 |

191 |
192 |
193 |
194 |
195 |
196 | 197 |
198 |
199 |

200 | Proporção de mulheres fantasmas:{" "} 201 | {Math.round( 202 | this.state.dadosRanking[2].porcent_zero * 100 203 | )}{" "} 204 | % 205 |

206 |
207 |
208 | 220 |
221 |
222 |
223 |
224 |
225 | )} 226 |
227 |
228 |
229 | 30% 230 |

231 | É o mínimo de candidatos de um gênero que um partido ou 232 | coligação deve ter. 233 |

234 | 235 |
236 |
237 |
238 |
239 |
240 |
241 | 242 |
243 |

Como colaborar?

244 |
245 |

246 | Compartilhe no twitter utilizando a hashtag{" "} 247 | #NaoNasciPraSerAOutra para que a outros conheçam quais 248 | são os partidos que possuem menos candidatas do sexo feminino 249 | e que candidatam mulheres apenas para o preenchimento dos 30%. 250 |

251 |
252 | 257 | Tweet! 258 | 259 | 260 |