├── packages ├── elections │ ├── README.md │ ├── assets │ │ ├── fonts │ │ │ ├── stylesheet.css │ │ │ ├── freesans-webfont.woff2 │ │ │ └── freesansbold-webfont.woff │ │ ├── icon.icns │ │ ├── icons │ │ │ ├── 128x128.png │ │ │ ├── 16x16.png │ │ │ ├── 24x24.png │ │ │ ├── 256x256.png │ │ │ ├── 32x32.png │ │ │ ├── 48x48.png │ │ │ ├── 512x512.png │ │ │ ├── 64x64.png │ │ │ ├── 96x96.png │ │ │ └── 1024x1024.png │ │ ├── entitlements.mac.plist │ │ └── assets.d.ts │ ├── .erb │ │ ├── mocks │ │ │ └── fileMock.js │ │ ├── configs │ │ │ ├── .eslintrc │ │ │ ├── webpack.config.eslint.ts │ │ │ ├── webpack.config.base.ts │ │ │ ├── webpack.paths.ts │ │ │ ├── webpack.config.renderer.dev.dll.ts │ │ │ └── webpack.config.main.prod.ts │ │ └── scripts │ │ │ ├── .eslintrc │ │ │ ├── delete-source-maps.js │ │ │ ├── link-modules.ts │ │ │ ├── check-node-env.js │ │ │ ├── clean.js │ │ │ ├── check-port-in-use.js │ │ │ ├── electron-rebuild.js │ │ │ ├── check-build-exists.ts │ │ │ ├── notarize.js │ │ │ └── check-native-dep.js │ ├── src │ │ ├── renderer │ │ │ ├── CidadeCargos.module.scss │ │ │ ├── Reset.module.scss │ │ │ ├── fonts │ │ │ │ ├── KumbhSans.ttf │ │ │ │ ├── KumbhSans-Black.ttf │ │ │ │ ├── KumbhSans-Bold.ttf │ │ │ │ ├── KumbhSans-Light.ttf │ │ │ │ ├── KumbhSans-Medium.ttf │ │ │ │ ├── KumbhSans-Thin.ttf │ │ │ │ ├── NimbusSansBold.ttf │ │ │ │ ├── NimbusSansLight.ttf │ │ │ │ ├── KumbhSans-Regular.ttf │ │ │ │ ├── KumbhSans-SemiBold.ttf │ │ │ │ ├── NimbusSansRegular.ttf │ │ │ │ ├── KumbhSans-ExtraBold.ttf │ │ │ │ ├── KumbhSans-ExtraLight.ttf │ │ │ │ ├── NimbusSansUltraLight.ttf │ │ │ │ ├── NimbusSansBoldExtended.ttf │ │ │ │ ├── NimbusSansLightExtended.ttf │ │ │ │ └── NimbusSansRegularExtended.ttf │ │ │ ├── index.tsx │ │ │ ├── index.ejs │ │ │ ├── screens │ │ │ │ ├── index.ts │ │ │ │ ├── Loader.module.scss │ │ │ │ ├── Loader.tsx │ │ │ │ ├── NulledVotesPage │ │ │ │ │ ├── transformers.ts │ │ │ │ │ └── NulledVotesPage.tsx │ │ │ │ ├── helpers.ts │ │ │ │ ├── NullBoxesPage │ │ │ │ │ └── transformers.ts │ │ │ │ ├── ExposedPage │ │ │ │ │ ├── ExposedPage.tsx │ │ │ │ │ └── transformers.ts │ │ │ │ └── SimuladorPage │ │ │ │ │ └── transformers.ts │ │ │ ├── features │ │ │ │ ├── navigation.tsx │ │ │ │ └── SmartPage.tsx │ │ │ ├── App.tsx │ │ │ ├── components │ │ │ │ ├── Navigation.module.scss │ │ │ │ ├── Bar.tsx │ │ │ │ ├── Bar.module.scss │ │ │ │ ├── helpers │ │ │ │ │ └── utils.ts │ │ │ │ ├── Dropper.module.scss │ │ │ │ └── Navigation.tsx │ │ │ └── helpers │ │ │ │ └── data.ts │ │ └── main │ │ │ ├── worker.js │ │ │ ├── util.ts │ │ │ ├── preload.js │ │ │ ├── api │ │ │ └── api.ts │ │ │ └── data │ │ │ └── data.ts │ ├── .gitattributes │ ├── .gitignore │ ├── release │ │ └── app │ │ │ └── package.json │ ├── tsconfig.json │ └── .eslintrc.js ├── ui │ ├── src │ │ ├── base │ │ │ ├── SelectorBase.tsx │ │ │ ├── CanvasLines.module.scss │ │ │ ├── Page.module.scss │ │ │ ├── Loader.tsx │ │ │ ├── Page.tsx │ │ │ ├── Section.module.scss │ │ │ ├── Spacer.tsx │ │ │ ├── ListItem.module.scss │ │ │ ├── Spacer.module.scss │ │ │ ├── Wrapper.module.scss │ │ │ ├── Empty.module.scss │ │ │ ├── Wrapper.tsx │ │ │ ├── Logo.tsx │ │ │ ├── Tags.module.scss │ │ │ ├── Elevation.tsx │ │ │ ├── Loader.module.scss │ │ │ ├── Empty.tsx │ │ │ ├── Tags.tsx │ │ │ ├── index.ts │ │ │ ├── Section.tsx │ │ │ ├── Elevation.module.scss │ │ │ ├── ListItem.tsx │ │ │ ├── Block.tsx │ │ │ ├── helpers.js │ │ │ ├── Logo.module.scss │ │ │ ├── Selector.module.scss │ │ │ ├── Button.tsx │ │ │ ├── ElevatedPressable.tsx │ │ │ ├── Button.module.scss │ │ │ └── LineChart.module.scss │ │ ├── dre │ │ │ ├── common │ │ │ │ ├── Image.module.scss │ │ │ │ ├── Screen.tsx │ │ │ │ ├── Controls.module.scss │ │ │ │ ├── Key.module.scss │ │ │ │ ├── Badge.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── Keyboard.module.scss │ │ │ │ ├── Person.tsx │ │ │ │ ├── Screen.module.scss │ │ │ │ ├── Box.tsx │ │ │ │ ├── Button.tsx │ │ │ │ ├── SimulatorLogs.module.scss │ │ │ │ ├── Keyboard.tsx │ │ │ │ ├── Badge.module.scss │ │ │ │ ├── Person.module.scss │ │ │ │ ├── Keypad.module.scss │ │ │ │ ├── Controls.tsx │ │ │ │ ├── Font.tsx │ │ │ │ ├── SimulatorSummary.tsx │ │ │ │ ├── SimulatorVotes.tsx │ │ │ │ ├── helpers.tsx │ │ │ │ ├── Button.module.scss │ │ │ │ ├── Font.module.scss │ │ │ │ ├── Box.module.scss │ │ │ │ ├── useCustomState.jsx │ │ │ │ ├── Key.tsx │ │ │ │ ├── SimulatorLogs.tsx │ │ │ │ ├── Keypad.tsx │ │ │ │ └── Image.tsx │ │ │ ├── index.ts │ │ │ ├── legacy │ │ │ │ ├── Simulador.module.scss │ │ │ │ ├── Elevation.tsx │ │ │ │ ├── Elevation.module.scss │ │ │ │ └── Button.module.old.scss │ │ │ ├── revisited │ │ │ │ ├── Simulador.module.scss │ │ │ │ └── Button.module.old.scss │ │ │ ├── hooks │ │ │ │ └── useCustomState.jsx │ │ │ ├── candidate.svg │ │ │ └── candidate.tsx │ │ └── module.ts │ ├── global.d.ts │ ├── tsconfig.json │ └── package.json ├── engine │ ├── src │ │ ├── data │ │ │ ├── README.md │ │ │ ├── alternative.js │ │ │ ├── eletronica.js │ │ │ ├── partidos.js │ │ │ ├── partidos copy.js │ │ │ └── alagoas.js │ │ ├── eleicoes │ │ │ ├── constants.js │ │ │ ├── investigations │ │ │ │ ├── transition-boxes.js │ │ │ │ ├── absent-boxes.js │ │ │ │ ├── index.js │ │ │ │ ├── city-info.js │ │ │ │ └── missing-positions.js │ │ │ └── printer │ │ │ │ └── utils.js │ │ └── index.js │ └── package.json ├── codex │ ├── src │ │ └── integrity │ │ │ ├── helpers │ │ │ ├── events.ts │ │ │ └── types.ts │ │ │ ├── interpretation │ │ │ ├── western-electoral-heritage.ts │ │ │ ├── brazilian-electoral-code.ts │ │ │ └── brazilian-electoral-court.ts │ │ │ └── __tests__ │ │ │ └── interpretation.ts │ ├── tsconfig.json │ └── package.json └── core │ ├── index.js │ ├── .eslintrc.js │ ├── utils │ ├── engine │ │ └── situations.js │ ├── transformers │ │ ├── chart.js │ │ ├── table.js │ │ ├── filters.js │ │ └── boxes.js │ └── transform.js │ └── package.json ├── assets ├── legado.png ├── screens.png └── prototipo.png ├── .vscode ├── extensions.json ├── settings.json ├── tasks.json └── launch.json ├── lerna.json ├── .editorconfig ├── .gitattributes ├── .gitignore ├── package.json ├── docs └── excluidos.md └── README.md /packages/elections/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui/src/base/SelectorBase.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/elections/assets/fonts/stylesheet.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ui/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@gavetaio/ui"; 2 | -------------------------------------------------------------------------------- /packages/elections/.erb/mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /assets/legado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/assets/legado.png -------------------------------------------------------------------------------- /assets/screens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/assets/screens.png -------------------------------------------------------------------------------- /assets/prototipo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/assets/prototipo.png -------------------------------------------------------------------------------- /packages/elections/src/renderer/CidadeCargos.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | } 4 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Image.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .container { 2 | display: block; 3 | } 4 | -------------------------------------------------------------------------------- /packages/ui/src/module.ts: -------------------------------------------------------------------------------- 1 | export * from "./base/index"; 2 | // @ts-ignore 3 | export * from "./dre/index"; 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "EditorConfig.EditorConfig"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/elections/assets/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icon.icns -------------------------------------------------------------------------------- /packages/elections/src/renderer/Reset.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | background-color: red; 4 | } 5 | -------------------------------------------------------------------------------- /packages/elections/assets/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/128x128.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/16x16.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/24x24.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/256x256.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/32x32.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/48x48.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/512x512.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/64x64.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/96x96.png -------------------------------------------------------------------------------- /packages/elections/assets/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/icons/1024x1024.png -------------------------------------------------------------------------------- /packages/engine/src/data/README.md: -------------------------------------------------------------------------------- 1 | Data coletada externamente ao repositório eleitoral (`TRE`), com o objetivo de complementar o estudo. 2 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0", 6 | "npmClient": "yarn", 7 | "useWorkspaces": true 8 | } -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans.ttf -------------------------------------------------------------------------------- /packages/elections/assets/fonts/freesans-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/fonts/freesans-webfont.woff2 -------------------------------------------------------------------------------- /packages/ui/src/base/CanvasLines.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | width: 800px; 4 | height: 600px; 5 | background-color: red; 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/dre/index.ts: -------------------------------------------------------------------------------- 1 | export { default as RevisitedBox } from "./revisited/Simulador"; 2 | export { default as LegacyBox } from "./legacy/Simulador"; 3 | -------------------------------------------------------------------------------- /packages/elections/assets/fonts/freesansbold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/assets/fonts/freesansbold-webfont.woff -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-Black.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-Bold.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-Light.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-Medium.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-Thin.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansBold.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansLight.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-Regular.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-SemiBold.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansRegular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansRegular.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-ExtraBold.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/KumbhSans-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/KumbhSans-ExtraLight.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansUltraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansUltraLight.ttf -------------------------------------------------------------------------------- /packages/elections/.erb/configs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansBoldExtended.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansBoldExtended.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansLightExtended.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansLightExtended.ttf -------------------------------------------------------------------------------- /packages/elections/src/renderer/fonts/NimbusSansRegularExtended.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavetaio/electio/HEAD/packages/elections/src/renderer/fonts/NimbusSansRegularExtended.ttf -------------------------------------------------------------------------------- /packages/elections/.erb/configs/webpack.config.eslint.ts: -------------------------------------------------------------------------------- 1 | /* eslint import/no-unresolved: off, import/no-self-import: off */ 2 | 3 | module.exports = require('./webpack.config.renderer.dev').default; 4 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App'; 4 | 5 | render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /packages/codex/src/integrity/helpers/events.ts: -------------------------------------------------------------------------------- 1 | export const NO_ACTION = 'no-action'; 2 | export const PARTIAL_ELECTION_REPEAT = 'partial-election-repeat'; 3 | export const FULL_ELECTION_REPEAT = 'full-election-repeat'; 4 | -------------------------------------------------------------------------------- /packages/engine/src/data/alternative.js: -------------------------------------------------------------------------------- 1 | const data = ` 2 | ESIO VICENTE DE MATOS, 00001943, PFL 3 | CANDIDO OTTONI, 00001717, PMDB 4 | ALBERTO OTTONI GUIMARAES, 00000068, PDT/PT 5 | Brancos, 38 6 | Nulos, 121 7 | `; 8 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "global-require": "off", 5 | "import/no-dynamic-require": "off", 6 | "import/no-extraneous-dependencies": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Screen.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Screen.module.scss"; 3 | 4 | const Screen = ({ children }: any) => ( 5 |
{children}
6 | ); 7 | 8 | export default Screen; 9 | -------------------------------------------------------------------------------- /packages/ui/src/base/Page.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | margin-left: var(--menu-width); 4 | min-height: 100vh; 5 | margin-top: var(--bar-height); 6 | padding: 32px; 7 | position: relative; 8 | z-index: 1; 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.exe binary 3 | *.png binary 4 | *.jpg binary 5 | *.jpeg binary 6 | *.ico binary 7 | *.icns binary 8 | *.eot binary 9 | *.otf binary 10 | *.ttf binary 11 | *.woff binary 12 | *.woff2 binary 13 | -------------------------------------------------------------------------------- /packages/elections/.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.exe binary 3 | *.png binary 4 | *.jpg binary 5 | *.jpeg binary 6 | *.ico binary 7 | *.icns binary 8 | *.eot binary 9 | *.otf binary 10 | *.ttf binary 11 | *.woff binary 12 | *.woff2 binary 13 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/constants.js: -------------------------------------------------------------------------------- 1 | export const RESULTADO = { 2 | partidos: {}, 3 | candidatos: {}, 4 | boxes: { 5 | maior: {}, 6 | menor: {}, 7 | }, 8 | problemas: [], 9 | coligacoes: {}, 10 | multipliers: null, 11 | cargos: {}, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/ui/src/base/Loader.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from './Loader.module.scss'; 3 | 4 | const Loader = () => { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | }; 11 | 12 | export default Loader; 13 | -------------------------------------------------------------------------------- /packages/ui/src/base/Page.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from './Page.module.scss'; 3 | 4 | const Page = ({ children }) => { 5 | const cls = [styles.container]; 6 | 7 | return
{children}
; 8 | }; 9 | 10 | export default Page; 11 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Controls.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .controls { 2 | width: 100%; 3 | display: flex; 4 | align-items: flex-end; 5 | justify-content: space-between; 6 | padding: 0 calc(var(--unit) * 4) calc(var(--unit) * 8); 7 | top: calc(var(--unit) * -1); 8 | position: relative; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/base/Section.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | padding-bottom: var(--unit-v-4); 4 | 5 | .header { 6 | padding-bottom: var(--unit-v-2); 7 | 8 | h4, 9 | h5, 10 | h3 { 11 | color: var(--text-alt); 12 | text-transform: uppercase; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Key.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .key { 2 | min-width: var(--key-width); 3 | height: var(--key-height); 4 | color: var(--white); 5 | margin: calc(var(--key-height) / 4); 6 | position: relative; 7 | 8 | p { 9 | margin: 0 0 0 calc(var(--key-height) / 10); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Electio — @gavetaio 6 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/delete-source-maps.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import rimraf from 'rimraf'; 3 | import webpackPaths from '../configs/webpack.paths'; 4 | 5 | export default function deleteSourceMaps() { 6 | rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map')); 7 | rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map')); 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/dre/legacy/Simulador.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | } 4 | 5 | .logs { 6 | min-height: 200px; 7 | } 8 | 9 | .restartButton { 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | justify-content: center; 14 | padding-top: var(--unit-v-1); 15 | padding-bottom: var(--unit-v-1); 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/dre/revisited/Simulador.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | } 4 | 5 | .logs { 6 | min-height: 200px; 7 | } 8 | 9 | .restartButton { 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | justify-content: center; 14 | padding-top: var(--unit-v-1); 15 | padding-bottom: var(--unit-v-1); 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/base/Spacer.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from './Spacer.module.scss'; 3 | 4 | const Spacer = ({ title }: any) => { 5 | const cls = [styles.container]; 6 | 7 | return ( 8 |
9 |
10 | {title &&
{title}
} 11 |
12 |
13 | ); 14 | }; 15 | 16 | export default Spacer; 17 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Badge.tsx: -------------------------------------------------------------------------------- 1 | import Font from "./Font"; 2 | // @ts-ignore 3 | import styles from "./Badge.module.scss"; 4 | 5 | const Badge = ({ title = "Badge" }) => ( 6 |
7 |
8 | 9 | {title} 10 | 11 |
12 |
13 | ); 14 | 15 | export default Badge; 16 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Font } from "./Font"; 2 | export { default as Keyboard } from "./Keyboard"; 3 | export { default as Button } from "./Button"; 4 | export { default as Screen } from "./Screen"; 5 | export { default as Box } from "./Box"; 6 | export { default as Keypad } from "./Keypad"; 7 | export { default as Image } from "./Image"; 8 | export { default as Person } from "./Person"; 9 | -------------------------------------------------------------------------------- /packages/elections/assets/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/link-modules.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import webpackPaths from '../configs/webpack.paths'; 3 | 4 | const srcNodeModulesPath = webpackPaths.srcNodeModulesPath; 5 | const appNodeModulesPath = webpackPaths.appNodeModulesPath 6 | 7 | if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) { 8 | fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/index.js: -------------------------------------------------------------------------------- 1 | export * from './utils/general'; 2 | export * from './utils/transform'; 3 | export * from './utils/transformers/eleicoes'; 4 | export * from './utils/transformers/exposed'; 5 | export * from './utils/transformers/boxes'; 6 | export * from './utils/transformers/table'; 7 | export * from './utils/transformers/chart'; 8 | export * from './utils/transformers/filters'; 9 | export * from './utils/transformers/resumo'; 10 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Keyboard.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .keyboard { 2 | align-items: center; 3 | justify-content: center; 4 | display: flex; 5 | flex-wrap: wrap; 6 | flex: 1; 7 | padding-top: calc(var(--unit) * 4); 8 | 9 | > div:nth-child(1) { 10 | width: calc(var(--key-width) * 5); 11 | display: flex; 12 | flex-wrap: wrap; 13 | align-self: center; 14 | justify-content: center; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Person.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Person.module.scss"; 3 | 4 | const Person = ({ className = null }) => { 5 | const cls = [styles.container]; 6 | 7 | if (className) { 8 | cls.push(className); 9 | } 10 | 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | ); 18 | }; 19 | 20 | export default Person; 21 | -------------------------------------------------------------------------------- /packages/elections/src/main/worker.js: -------------------------------------------------------------------------------- 1 | const { Worker, isMainThread, parentPort } = require('worker_threads'); 2 | 3 | module.exports = () => { 4 | if (isMainThread) { 5 | const worker = new Worker(__filename); 6 | worker.once('message', (message) => {}); 7 | worker.postMessage('Hello, world!'); 8 | } else { 9 | parentPort.once('message', (message) => { 10 | parentPort.postMessage(message); 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SimuladorPage } from './SimuladorPage/SimuladorPage'; 2 | export { default as HomePage } from './HomePage'; 3 | export { default as NullBoxesPage } from './NullBoxesPage/NullBoxesPage'; 4 | export { default as ExposedPage } from './ExposedPage/ExposedPage'; 5 | export { default as NulledVotesPage } from './NulledVotesPage/NulledVotesPage'; 6 | export { default as AboutPage } from './AboutPage'; 7 | -------------------------------------------------------------------------------- /packages/ui/src/base/ListItem.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: block; 3 | margin-bottom: 24px; 4 | background-color: var(--background-darker); 5 | padding: 12px 16px; 6 | border-radius: 4px; 7 | overflow: hidden; 8 | 9 | h3 { 10 | color: var(--text-alt); 11 | } 12 | } 13 | 14 | .tags { 15 | margin-top: 4px; 16 | } 17 | 18 | .table { 19 | margin-top: 8px; 20 | 21 | section { 22 | margin-bottom: 8px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/src/base/Spacer.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | align-items: center; 4 | margin: 48px auto 32px; 5 | width: 80%; 6 | 7 | h6 { 8 | margin: 0 12px; 9 | text-transform: uppercase; 10 | color: var(--background-lighter); 11 | } 12 | 13 | hr { 14 | display: flex; 15 | flex: 1; 16 | border: none; 17 | height: 0; 18 | border-bottom: 1px solid var(--background-lighter); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/src/base/Wrapper.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 1480px; 3 | margin: 0 auto; 4 | width: 100%; 5 | 6 | > section { 7 | &:nth-last-child(1) { 8 | margin-bottom: 0px; 9 | } 10 | } 11 | } 12 | 13 | .flexRow { 14 | display: flex; 15 | flex-direction: row; 16 | align-items: center; 17 | justify-content: space-around; 18 | } 19 | 20 | .padding { 21 | padding-left: 32px; 22 | padding-right: 32px; 23 | } 24 | -------------------------------------------------------------------------------- /packages/ui/src/base/Empty.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: var(--unit-h-3) var(--unit-v-3); 3 | text-align: center; 4 | font-weight: 600; 5 | 6 | h5 { 7 | color: var(--text-lighter); 8 | } 9 | 10 | footer { 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | margin-top: var(--unit-v-3); 15 | } 16 | 17 | svg { 18 | fill: var(--text-lighter); 19 | margin-bottom: var(--unit-v-1); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/base/Wrapper.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // @ts-ignore 3 | import styles from './Wrapper.module.scss'; 4 | 5 | const Wrapper = ({ children, flexRow, padding }: any) => { 6 | const cls = [styles.container]; 7 | if (flexRow) { 8 | cls.push(styles.flexRow); 9 | } 10 | 11 | if (padding) { 12 | cls.push(styles.padding); 13 | } 14 | 15 | return
{children}
; 16 | }; 17 | 18 | export default Wrapper; 19 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/check-node-env.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default function checkNodeEnv(expectedEnv) { 4 | if (!expectedEnv) { 5 | throw new Error('"expectedEnv" not set'); 6 | } 7 | 8 | if (process.env.NODE_ENV !== expectedEnv) { 9 | console.log( 10 | chalk.whiteBright.bgRed.bold( 11 | `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` 12 | ) 13 | ); 14 | process.exit(2); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/codex/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./lib", 4 | "baseUrl": ".", 5 | "target": "es2021", 6 | "module": "commonjs", 7 | "lib": ["dom", "esnext"], 8 | "declaration": true, 9 | "declarationMap": true, 10 | "pretty": true, 11 | "sourceMap": true, 12 | "noImplicitAny": false, 13 | "esModuleInterop": true, 14 | "allowSyntheticDefaultImports": true, 15 | "resolveJsonModule": true, 16 | "allowJs": true, 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['airbnb', 'prettier'], 3 | parserOptions: { 4 | ecmaVersion: 2020, 5 | }, 6 | rules: { 7 | 'import/no-extraneous-dependencies': 'off', 8 | 'react/react-in-jsx-scope': 'off', 9 | 'react/jsx-props-no-spreading': 'off', 10 | '@typescript-eslint/no-explicit-any': 'off', 11 | 'import/prefer-default-export': 'off', 12 | 'arrow-body-style': 'off', 13 | 'no-shadow': 'off', 14 | 'no-continue': 'off', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | *.lock 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Coverage directory used by tools like istanbul 12 | coverage 13 | .eslintcache 14 | 15 | # Dependency directory 16 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 17 | node_modules 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | release/app/dist 23 | release/build 24 | .erb/dll 25 | 26 | .idea 27 | npm-debug.log.* 28 | *.css.d.ts 29 | *.sass.d.ts 30 | *.scss.d.ts 31 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/clean.js: -------------------------------------------------------------------------------- 1 | import rimraf from 'rimraf'; 2 | import webpackPaths from '../configs/webpack.paths.ts'; 3 | import process from 'process'; 4 | 5 | const args = process.argv.slice(2); 6 | const commandMap = { 7 | dist: webpackPaths.distPath, 8 | release: webpackPaths.releasePath, 9 | dll: webpackPaths.dllPath, 10 | }; 11 | 12 | args.forEach((x) => { 13 | const pathToRemove = commandMap[x]; 14 | if (pathToRemove !== undefined) { 15 | rimraf.sync(pathToRemove); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Screen.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .screen { 2 | background-color: var(--screen); 3 | transition: background-color 50ms ease-in-out; 4 | width: var(--screen-width); 5 | height: var(--screen-height); 6 | position: relative; 7 | margin: calc(var(--unit) * 6); 8 | border: 1px solid var(--border); 9 | padding: calc(var(--unit) / 4); 10 | flex-shrink: 0; 11 | display: flex; 12 | 13 | h2, 14 | p, 15 | strong { 16 | transition: color 50ms ease-in-out; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/check-port-in-use.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import detectPort from 'detect-port'; 3 | 4 | const port = process.env.PORT || '1212'; 5 | 6 | detectPort(port, (err, availablePort) => { 7 | if (port !== String(availablePort)) { 8 | throw new Error( 9 | chalk.whiteBright.bgRed.bold( 10 | `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start` 11 | ) 12 | ); 13 | } else { 14 | process.exit(0); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /packages/elections/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage directory used by tools like istanbul 11 | coverage 12 | .eslintcache 13 | 14 | # Dependency directory 15 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 16 | node_modules 17 | 18 | # OSX 19 | .DS_Store 20 | 21 | release/app/dist 22 | release/build 23 | .erb/dll 24 | 25 | .idea 26 | npm-debug.log.* 27 | *.css.d.ts 28 | *.sass.d.ts 29 | *.scss.d.ts 30 | -------------------------------------------------------------------------------- /packages/ui/src/base/Logo.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Logo.module.scss"; 3 | 4 | export default function Logo({ canClip = true, circled = false }: any) { 5 | const cls = [styles.container]; 6 | 7 | if (!canClip) { 8 | cls.push(styles.noclip); 9 | } 10 | 11 | if (circled) { 12 | cls.push(styles.circled); 13 | } 14 | 15 | return ( 16 |
17 |
18 | 19 | 20 | 21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./lib", 4 | "baseUrl": ".", 5 | "target": "es2021", 6 | "module": "commonjs", 7 | "lib": ["dom", "esnext"], 8 | "declaration": true, 9 | "declarationMap": true, 10 | "jsx": "react-jsx", 11 | "pretty": true, 12 | "sourceMap": true, 13 | "noImplicitAny": false, 14 | "moduleResolution": "node", 15 | "esModuleInterop": true, 16 | "allowSyntheticDefaultImports": true, 17 | "resolveJsonModule": true, 18 | "allowJs": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/Loader.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | z-index: 4001; 8 | background-color: rgba(0, 0, 0, 0.75); 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | } 13 | 14 | .content { 15 | display: block; 16 | text-align: center; 17 | 18 | h3 { 19 | margin-top: var(--unit-xxl); 20 | margin-bottom: var(--unit-s); 21 | } 22 | small { 23 | font-family: 'Courier New', Courier, monospace; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/investigations/transition-boxes.js: -------------------------------------------------------------------------------- 1 | const { forEachList } = require("../helpers"); 2 | const { LoggerSingleton } = require("../../log"); 3 | 4 | const { log } = LoggerSingleton.getInstance(); 5 | 6 | export const investigateTransitionBoxes = ({ resultados, boxes, callback }) => { 7 | let total = 0; 8 | 9 | forEachList(boxes, (id, data) => { 10 | const { transition } = data; 11 | if (!transition) { 12 | return; 13 | } 14 | total += 1; 15 | }); 16 | 17 | if (total) { 18 | log(`TRANSITION BOXES ${total}`); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /packages/elections/src/main/util.ts: -------------------------------------------------------------------------------- 1 | import { URL } from 'url'; 2 | import path from 'path'; 3 | 4 | export let resolveHtmlPath: (htmlFileName: string) => string; 5 | 6 | if (process.env.NODE_ENV === 'development') { 7 | const port = process.env.PORT || 1212; 8 | resolveHtmlPath = (htmlFileName: string) => { 9 | const url = new URL(`http://localhost:${port}`); 10 | url.pathname = htmlFileName; 11 | return url.href; 12 | }; 13 | } else { 14 | resolveHtmlPath = (htmlFileName: string) => { 15 | return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | ".eslintrc": "jsonc", 4 | ".prettierrc": "jsonc", 5 | ".eslintignore": "ignore" 6 | }, 7 | 8 | "javascript.validate.enable": false, 9 | "javascript.format.enable": false, 10 | "typescript.format.enable": false, 11 | 12 | "search.exclude": { 13 | ".git": true, 14 | ".eslintcache": true, 15 | ".erb/dll": true, 16 | "release/{build,app/dist}": true, 17 | "node_modules": true, 18 | "npm-debug.log.*": true, 19 | "test/**/__snapshots__": true, 20 | "package-lock.json": true, 21 | "*.{css,sass,scss}.d.ts": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Box.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @ts-ignore 3 | import styles from "./Box.module.scss"; 4 | 5 | const Box = React.forwardRef(({ children, className }: any, ref: any) => { 6 | const cls = [styles.container, "electio-sim"]; 7 | 8 | if (className) { 9 | cls.push(className); 10 | } 11 | 12 | if (className) 13 | return ( 14 |
15 |
16 |
17 | {children} 18 |
19 |
20 |
21 | ); 22 | }); 23 | 24 | export default Box; 25 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Button.tsx: -------------------------------------------------------------------------------- 1 | import { ElevatedPressable } from "../../base"; 2 | import Font from "./Font"; 3 | // @ts-ignore 4 | import styles from "./Button.module.scss"; 5 | 6 | const Button = ({ onLongPress, onClick, type = "white", children }: any) => { 7 | const cls = [styles.button, styles[type]]; 8 | 9 | return ( 10 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default Button; 23 | -------------------------------------------------------------------------------- /packages/elections/release/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-react-boilerplate", 3 | "productName": "electron-react-boilerplate", 4 | "version": "4.3.1", 5 | "description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development", 6 | "main": "./dist/main/main.js", 7 | "scripts": { 8 | "electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js", 9 | "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts", 10 | "postinstall": "npm run electron-rebuild && npm run link-modules" 11 | }, 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /packages/elections/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "commonjs", 5 | "lib": ["dom", "esnext"], 6 | "declaration": true, 7 | "declarationMap": true, 8 | "jsx": "react-jsx", 9 | "pretty": true, 10 | "sourceMap": true, 11 | "baseUrl": "./src", 12 | "noImplicitAny": false, 13 | "moduleResolution": "node", 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "resolveJsonModule": true, 17 | "allowJs": true, 18 | "outDir": "release/app/dist" 19 | }, 20 | "exclude": ["test", "release/build", "release/app/dist", ".erb/dll"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/base/Tags.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | list-style: none; 3 | display: flex; 4 | flex-direction: row; 5 | 6 | li { 7 | display: block; 8 | background-color: var(--background); 9 | padding: 0 8px; 10 | border-radius: 2px; 11 | display: flex; 12 | flex-direction: row; 13 | align-items: center; 14 | margin-right: 4px; 15 | margin-bottom: 4px; 16 | } 17 | 18 | .danger { 19 | background-color: var(--background-rouge); 20 | } 21 | 22 | .warning { 23 | background-color: var(--background-jaune); 24 | } 25 | } 26 | 27 | .number { 28 | font-weight: 800; 29 | margin-right: 4px; 30 | } 31 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "label": "Start Webpack Dev", 7 | "script": "start:renderer", 8 | "options": { 9 | "cwd": "${workspaceFolder}" 10 | }, 11 | "isBackground": true, 12 | "problemMatcher": { 13 | "owner": "custom", 14 | "pattern": { 15 | "regexp": "____________" 16 | }, 17 | "background": { 18 | "activeOnStart": true, 19 | "beginsPattern": "Compiling\\.\\.\\.$", 20 | "endsPattern": "(Compiled successfully|Failed to compile)\\.$" 21 | } 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/elections/assets/assets.d.ts: -------------------------------------------------------------------------------- 1 | type Styles = Record; 2 | 3 | declare module '*.svg' { 4 | const content: string; 5 | export default content; 6 | } 7 | 8 | declare module '*.png' { 9 | const content: string; 10 | export default content; 11 | } 12 | 13 | declare module '*.jpg' { 14 | const content: string; 15 | export default content; 16 | } 17 | 18 | declare module '*.scss' { 19 | const content: Styles; 20 | export default content; 21 | } 22 | 23 | declare module '*.sass' { 24 | const content: Styles; 25 | export default content; 26 | } 27 | 28 | declare module '*.css' { 29 | const content: Styles; 30 | export default content; 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gavetaio/electio", 3 | "version": "1.0.0", 4 | "description": "@gavetaio — investigação eleitoral", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "private": true, 8 | "workspaces": [ 9 | "packages/*" 10 | ], 11 | "scripts": { 12 | "start": "lerna run --parallel start", 13 | "codex:integrity-test": "lerna run --parallel --scope @gavetaio/codex test", 14 | "build": "lerna run --parallel --scope @gavetaio/election-app build", 15 | "post": "lerna run --parallel --scope @gavetaio/election-app neverpostinstall" 16 | }, 17 | "devDependencies": { 18 | "lerna": "^4.0.0", 19 | "typescript": "^4.5.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/utils/engine/situations.js: -------------------------------------------------------------------------------- 1 | export const isRemovido = (situacao) => { 2 | if (situacao > 1) { 3 | return true; 4 | } 5 | return false; 6 | }; 7 | 8 | export const isRecursado = ({ urna, pleito, atual }) => { 9 | if (pleito?.match(/sub(.)j[uú]dice/gim)) { 10 | return true; 11 | } 12 | 13 | if ( 14 | urna.match(/^indeferido(.*)recurso$/gim) && 15 | !pleito.match(/^deferido/gim) 16 | ) { 17 | return true; 18 | } 19 | 20 | return false; 21 | }; 22 | 23 | export const isUrnado = ({ pleito }) => { 24 | if ( 25 | pleito.match(/^(indeferido|cassado|ren[úu]ncia|falecido|cancelado)$/gim) 26 | ) { 27 | return true; 28 | } 29 | return false; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/ui/src/base/Elevation.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Elevation.module.scss"; 3 | 4 | const Elevation = ({ 5 | children, 6 | caseColor, 7 | darkColor, 8 | pressed = false, 9 | }: any) => { 10 | const style: any = { 11 | "--elevation-bg-case": caseColor, 12 | "--elevation-bg-dark": darkColor, 13 | "--elevation-state": pressed ? "0.75" : "0", 14 | }; 15 | 16 | const cls = [styles.container]; 17 | 18 | if (pressed) { 19 | cls.push(styles.pressed); 20 | } 21 | 22 | return ( 23 |
24 |
{children}
25 |
26 | ); 27 | }; 28 | 29 | export default Elevation; 30 | -------------------------------------------------------------------------------- /packages/ui/src/base/Loader.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | height: 72px; 6 | 7 | span { 8 | display: block; 9 | border: 4px solid #fff; 10 | opacity: 1; 11 | border-radius: 50%; 12 | animation: ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; 13 | } 14 | 15 | span:nth-child(2) { 16 | animation-delay: -0.5s; 17 | } 18 | } 19 | 20 | @keyframes ripple { 21 | 0% { 22 | top: 36px; 23 | left: 36px; 24 | width: 0; 25 | height: 0; 26 | opacity: 1; 27 | } 28 | 100% { 29 | top: 0px; 30 | left: 0px; 31 | width: 72px; 32 | height: 72px; 33 | opacity: 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/SimulatorLogs.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .container { 2 | display: block; 3 | background-color: var(--background-dark); 4 | } 5 | 6 | :global(#sim-urna) .item { 7 | display: flex; 8 | align-items: center; 9 | 10 | li, 11 | span, 12 | strong, 13 | p, 14 | label { 15 | color: var(--text-primary); 16 | } 17 | 18 | label { 19 | min-width: 300px; 20 | } 21 | 22 | ul { 23 | list-style: none; 24 | } 25 | 26 | li { 27 | display: inline-block; 28 | margin-right: 8px; 29 | background-color: var(--background); 30 | padding: 2px 4px; 31 | border-radius: 4px; 32 | 33 | span { 34 | margin-right: 4px; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/Loader.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { LayoutContext } from 'renderer/context/layout'; 3 | import styles from './Loader.module.scss'; 4 | import { Loader } from '@gavetaio/ui'; 5 | 6 | const LoaderScreen = () => { 7 | const { getLayout }: any = useContext(LayoutContext); 8 | const { loader } = getLayout(); 9 | 10 | if (!loader?.visible) { 11 | return null; 12 | } 13 | 14 | return ( 15 |
16 |
17 | 18 |

{loader.title}

19 | {loader.text} 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default LoaderScreen; 26 | -------------------------------------------------------------------------------- /packages/ui/src/base/Empty.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Empty.module.scss"; 3 | import Button from "./Button"; 4 | import { DesktopDownloadIcon } from "@primer/octicons-react"; 5 | 6 | const Empty = ({ 7 | message = "Para visualizar esta página, carregue os dados de pelo menos um ciclo eleitoral com problemas.", 8 | action = null, 9 | label = "Carregar Dados", 10 | Icon = DesktopDownloadIcon, 11 | }) => { 12 | return ( 13 |
14 | {Icon && } 15 |
{message}
16 | {action && ( 17 |
18 | 19 |
20 | )} 21 |
22 | ); 23 | }; 24 | 25 | export default Empty; 26 | -------------------------------------------------------------------------------- /packages/ui/src/dre/legacy/Elevation.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties } from "react"; 2 | // @ts-ignore 3 | import styles from "./Elevation.module.scss"; 4 | 5 | const Elevation = ({ 6 | children, 7 | caseColor, 8 | darkColor, 9 | pressed = false, 10 | }: any) => { 11 | const style = { 12 | "--elevation-bg-case": caseColor, 13 | "--elevation-bg-dark": darkColor, 14 | "--elevation-state": pressed ? "0.75" : "0", 15 | } as CSSProperties; 16 | 17 | const cls = [styles.container]; 18 | 19 | if (pressed) { 20 | cls.push(styles.pressed); 21 | } 22 | 23 | return ( 24 |
25 |
{children}
26 |
27 | ); 28 | }; 29 | 30 | export default Elevation; 31 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/electron-rebuild.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { execSync } from 'child_process'; 3 | import fs from 'fs'; 4 | import { dependencies } from '../../release/app/package.json'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | if ( 8 | Object.keys(dependencies || {}).length > 0 && 9 | fs.existsSync(webpackPaths.appNodeModulesPath) 10 | ) { 11 | const electronRebuildCmd = 12 | '../../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; 13 | const cmd = 14 | process.platform === 'win32' 15 | ? electronRebuildCmd.replace(/\//g, '\\') 16 | : electronRebuildCmd; 17 | execSync(cmd, { 18 | cwd: webpackPaths.appPath, 19 | stdio: 'inherit', 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/base/Tags.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/destructuring-assignment */ 2 | // @ts-ignore 3 | import styles from './Tags.module.scss'; 4 | 5 | export const Tag = ({ label, type, number = null }: any) => { 6 | const cls = []; 7 | if (type) { 8 | cls.push(styles[type]); 9 | } 10 | return ( 11 |
  • 12 | {number !== null && {number}} 13 | {label} 14 |
  • 15 | ); 16 | }; 17 | 18 | const Tags = ({ items }: any) => { 19 | return ( 20 | 26 | ); 27 | }; 28 | 29 | export default Tags; 30 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Electron: Main", 6 | "type": "node", 7 | "request": "launch", 8 | "protocol": "inspector", 9 | "runtimeExecutable": "npm", 10 | "runtimeArgs": [ 11 | "run start:main --inspect=5858 --remote-debugging-port=9223" 12 | ], 13 | "preLaunchTask": "Start Webpack Dev" 14 | }, 15 | { 16 | "name": "Electron: Renderer", 17 | "type": "chrome", 18 | "request": "attach", 19 | "port": 9223, 20 | "webRoot": "${workspaceFolder}", 21 | "timeout": 15000 22 | } 23 | ], 24 | "compounds": [ 25 | { 26 | "name": "Electron: All", 27 | "configurations": ["Electron: Main", "Electron: Renderer"] 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packages/engine/src/data/eletronica.js: -------------------------------------------------------------------------------- 1 | const data1996 = `AM;MANAUS 2 | AP;MACAPA 3 | BA;FEIRA DE SANTANA;SALVADOR 4 | CE;FORTALEZA 5 | ES;VITORIA 6 | GO;GOIANIA 7 | MA;SAO LUIS 8 | MG;UBERLANDIA;BELO HORIZONTE;CONTAGEM;JUIZ DE FORA 9 | MS;CAMPO GRANDE 10 | MT;CUIABA 11 | PA;BELEM 12 | PB;CAMPINA GRANDE;JOAO PESSOA 13 | PE;JABOATAO;OLINDA;RECIFE 14 | PI;TERESINA 15 | PR;CURITIBA;LONDRINA 16 | RJ;NOVA IGUACU;BELFORD ROXO;CAMPOS;DUQUE DE CAXIAS;SAO GONCALO;SAO JOAO DE MERITI;RIO DE JANEIRO;NITEROI 17 | RN;NATAL 18 | RO;PORTO VELHO 19 | RR;BOA VISTA 20 | RS;CAXIAS DO SUL;PELOTAS;PORTO ALEGRE 21 | SC;JOINVILLE;BRUSQUE;FLORIANOPOLIS 22 | SE;ARACAJU 23 | SP;DIADEMA;GUARULHOS;JUNDIAI;OSASCO;RIBEIRAO PRETO;SANTO ANDRE;SANTOS;SAO BERNARDO DO CAMPO;SAO JOSE DO RIO PRETO;SAO JOSE DOS CAMPOS;SAO PAULO;SOROCABA;CAMPINAS 24 | TO;PALMAS`; 25 | -------------------------------------------------------------------------------- /packages/codex/src/integrity/interpretation/western-electoral-heritage.ts: -------------------------------------------------------------------------------- 1 | import { ElectoralIntegrityData } from '../helpers/types'; 2 | import { 3 | NO_ACTION, 4 | PARTIAL_ELECTION_REPEAT, 5 | FULL_ELECTION_REPEAT, 6 | } from '../helpers/events'; 7 | 8 | class WesternElectoralHeritage { 9 | shouldRepeatElection(electionData: ElectoralIntegrityData) { 10 | const { 11 | canInvalidatedVotesChangeElectionResult, 12 | canPartialElectionRepeatRemedyElectionResult, 13 | } = electionData; 14 | 15 | if (canInvalidatedVotesChangeElectionResult === false) { 16 | return NO_ACTION; 17 | } 18 | 19 | if (canPartialElectionRepeatRemedyElectionResult) { 20 | return PARTIAL_ELECTION_REPEAT; 21 | } 22 | 23 | return FULL_ELECTION_REPEAT; 24 | } 25 | } 26 | 27 | export default WesternElectoralHeritage; 28 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/check-build-exists.ts: -------------------------------------------------------------------------------- 1 | // Check if the renderer and main bundles are built 2 | import path from 'path'; 3 | import chalk from 'chalk'; 4 | import fs from 'fs'; 5 | import webpackPaths from '../configs/webpack.paths'; 6 | 7 | const mainPath = path.join(webpackPaths.distMainPath, 'main.js'); 8 | const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js'); 9 | 10 | if (!fs.existsSync(mainPath)) { 11 | throw new Error( 12 | chalk.whiteBright.bgRed.bold( 13 | 'The main process is not built yet. Build it by running "npm run build:main"' 14 | ) 15 | ); 16 | } 17 | 18 | if (!fs.existsSync(rendererPath)) { 19 | throw new Error( 20 | chalk.whiteBright.bgRed.bold( 21 | 'The renderer process is not built yet. Build it by running "npm run build:renderer"' 22 | ) 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/src/base/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button } from "./Button"; 2 | export { default as ListItem } from "./ListItem"; 3 | export { default as Loader } from "./Loader"; 4 | export { default as Logo } from "./Logo"; 5 | export { default as Page } from "./Page"; 6 | export { default as Selector } from "./Selector"; 7 | export { default as Spacer } from "./Spacer"; 8 | export { default as Table } from "./Table"; 9 | export { default as Tags } from "./Tags"; 10 | export { default as Wrapper } from "./Wrapper"; 11 | export { default as Elevation } from "./Elevation"; 12 | export { default as LineChart } from "./LineChart"; 13 | export { default as Block } from "./Block"; 14 | export { default as ElevatedPressable } from "./ElevatedPressable"; 15 | export { default as Section } from "./Section"; 16 | export { default as Empty } from "./Empty"; 17 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Keyboard.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Keyboard.module.scss"; 3 | import Key from "./Key"; 4 | const KEYS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]; 5 | 6 | const Keyboard = ({ onKeyPress, controls, handleAction = null }: any) => { 7 | const onKeyPressHandler = (number) => { 8 | if (!handleAction) { 9 | onKeyPress(number); 10 | return; 11 | } 12 | 13 | handleAction(onKeyPress(number)); 14 | }; 15 | 16 | return ( 17 |
    18 |
    19 | {KEYS.map((key) => ( 20 | 21 | {key} 22 | 23 | ))} 24 |
    25 | {controls()} 26 |
    27 | ); 28 | }; 29 | 30 | export default Keyboard; 31 | -------------------------------------------------------------------------------- /packages/engine/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gavetaio/engine", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "module": "src/index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "@gavetaio", 11 | "license": "ISC", 12 | "dependencies": { 13 | "array-unique": "^0.3.2", 14 | "react": "^18.1.0", 15 | "react-dom": "^18.1.0" 16 | }, 17 | "devDependencies": { 18 | "eslint": "^8.6.0", 19 | "eslint-config-airbnb-base": "^15.0.0", 20 | "eslint-config-erb": "^4.0.0-alpha.0", 21 | "eslint-plugin-compat": "^4.0.1", 22 | "eslint-plugin-import": "^2.25.4", 23 | "eslint-plugin-jsx-a11y": "^6.5.1", 24 | "eslint-plugin-promise": "^6.0.0", 25 | "eslint-plugin-react": "^7.28.0", 26 | "eslint-plugin-react-hooks": "^4.3.0", 27 | "typescript-plugin-css-modules": "^3.4.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/utils/transformers/chart.js: -------------------------------------------------------------------------------- 1 | export const chartObjectToData = (chart) => { 2 | const keys = Object.keys(chart); 3 | 4 | const lines = []; 5 | if (keys?.length) { 6 | keys.forEach((key) => { 7 | const line = { label: key, points: [] }; 8 | const keys = Object.keys(chart[key]); 9 | keys.forEach((k) => { 10 | const { count, total } = chart[key][k]; 11 | let value = count; 12 | if (total) { 13 | value = count / total; 14 | } 15 | line.points.push([k, value]); 16 | }); 17 | if (line?.points?.length) { 18 | lines.push(line); 19 | } 20 | }); 21 | } 22 | 23 | return { data: lines }; 24 | }; 25 | 26 | export const createChartObject = ({ x, y, data }) => { 27 | const dataObj = Array.isArray(data) ? { data } : chartObjectToData(data); 28 | return { 29 | ...dataObj, 30 | labelX: x, 31 | labelY: y, 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/codex/src/integrity/helpers/types.ts: -------------------------------------------------------------------------------- 1 | export type ElectoralIntegrityVotes = { 2 | blank: number; 3 | anulled: number; 4 | invalidated: number; 5 | valid: number; 6 | nominal: number; 7 | party: number; 8 | total: number; 9 | }; 10 | 11 | export type ElectoralIntegrityData = { 12 | name: string; 13 | type: 'majoritarian' | 'proportional'; 14 | voters: number; 15 | votes: ElectoralIntegrityVotes; 16 | repeatEvent: 'partial-election-repeat' | 'full-election-repeat' | 'no-action'; 17 | pollingStations: number; 18 | invalidatedPollingStations: number; 19 | canInvalidatedVotesChangeElectionResult: boolean; 20 | canPartialElectionRepeatRemedyElectionResult: boolean; 21 | minimalVoteLossToChangeElectoralResult: null | number; 22 | wasMajoritarianElectedCandidateInvalidated?: undefined | boolean; 23 | }; 24 | 25 | export interface ElectoralIntegrityClass { 26 | shouldRepeatElection: (electionData: ElectoralIntegrityData) => string; 27 | } 28 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/notarize.js: -------------------------------------------------------------------------------- 1 | const { notarize } = require('electron-notarize'); 2 | const { build } = require('../../package.json'); 3 | 4 | exports.default = async function notarizeMacos(context) { 5 | const { electronPlatformName, appOutDir } = context; 6 | if (electronPlatformName !== 'darwin') { 7 | return; 8 | } 9 | 10 | if (!process.env.CI) { 11 | console.warn('Skipping notarizing step. Packaging is not running in CI'); 12 | return; 13 | } 14 | 15 | if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) { 16 | console.warn('Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set'); 17 | return; 18 | } 19 | 20 | const appName = context.packager.appInfo.productFilename; 21 | 22 | await notarize({ 23 | appBundleId: build.appId, 24 | appPath: `${appOutDir}/${appName}.app`, 25 | appleId: process.env.APPLE_ID, 26 | appleIdPassword: process.env.APPLE_ID_PASS, 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Badge.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .badge { 2 | --badge-height: calc(var(--unit) * 24); 3 | --badge-gap: calc(var(--unit) * 4); 4 | --font-badge: 2; 5 | 6 | width: 100%; 7 | height: var(--badge-height); 8 | display: flex; 9 | align-items: center; 10 | border: none; 11 | justify-content: center; 12 | padding: 0 calc(var(--unit) * 6); 13 | 14 | h2 { 15 | text-transform: uppercase; 16 | text-align: left; 17 | padding: calc(var(--unit) * 2) calc(var(--unit) * 8); 18 | padding-top: calc(var(--unit) * 2 + 2px); 19 | border-bottom: 2px solid #000; 20 | background-color: var(--color-key); 21 | border-radius: 4px; 22 | 23 | &:nth-child(1) { 24 | letter-spacing: calc(var(--font-body) * -0.05); 25 | font-weight: 600; 26 | } 27 | &:nth-child(2) { 28 | letter-spacing: calc(var(--font-body) * -0.05); 29 | font-weight: 400; 30 | margin-top: var(--unit); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Person.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .container { 2 | --person-width: calc(var(--thumb-width) - 2px); 3 | --person-height: calc(var(--thumb-height) - 2px); 4 | 5 | display: block; 6 | height: var(--person-height); 7 | width: var(--person-width); 8 | background-color: transparent; 9 | position: relative; 10 | overflow: hidden; 11 | 12 | span { 13 | display: block; 14 | position: absolute; 15 | } 16 | 17 | span:nth-child(2) { 18 | display: block; 19 | background-color: #cfcfcf; 20 | border-radius: 50%; 21 | left: calc(var(--person-width) * 0.25); 22 | top: calc(var(--person-height) * 0.075); 23 | width: calc(var(--person-width) * 0.5); 24 | height: calc(var(--person-width) * 0.5); 25 | } 26 | 27 | span:nth-child(3) { 28 | display: block; 29 | background-color: #999999; 30 | height: 80%; 31 | width: 90%; 32 | bottom: -35%; 33 | left: 5%; 34 | border-top-right-radius: 50%; 35 | border-top-left-radius: 50%; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Keypad.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .keypad { 2 | --dre-font-family: var(--keypad-font-family); 3 | background-color: var(--keypad); 4 | position: relative; 5 | margin: calc(var(--unit) * 6); 6 | border: calc(var(--unit) / 2) solid var(--border); 7 | width: var(--keypad-width); 8 | height: 100%; 9 | 10 | > header { 11 | height: calc(var(--unit) * 20); 12 | background-color: white; 13 | font-weight: bold; 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | 18 | h2 { 19 | font-weight: 600; 20 | } 21 | } 22 | 23 | > div { 24 | flex: 1; 25 | } 26 | } 27 | 28 | .carbon { 29 | background: radial-gradient(black 6%, transparent 12%) 0 0, 30 | radial-gradient(black 6%, transparent 12%) 6px 6px, 31 | radial-gradient(rgba(255, 255, 255, 0.1) 6%, transparent 10%) 0 1px, 32 | radial-gradient(rgba(255, 255, 255, 0.1) 6%, transparent 10%) 6px 7px; 33 | background-color: #282828; 34 | background-size: 12px 12px; 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/utils/transform.js: -------------------------------------------------------------------------------- 1 | import { deepClone } from './general'; 2 | 3 | export const transformGraphLegacy = (chart) => { 4 | if (!chart?.data) { 5 | return null; 6 | } 7 | 8 | const cloned = deepClone(chart); 9 | 10 | if (cloned?.labels?.length) { 11 | const lines = []; 12 | cloned.data.forEach((item) => { 13 | const line = []; 14 | cloned.labels.forEach((label, index) => { 15 | line.push([label, item.data[index]]); 16 | }); 17 | lines.push(line); 18 | }); 19 | 20 | cloned.data = lines; 21 | } 22 | 23 | return cloned; 24 | }; 25 | 26 | export const getDataObject = (list, initial) => { 27 | const getInitial = (item) => { 28 | if (initial?.length) { 29 | return initial.indexOf(item) !== -1; 30 | } 31 | if (!initial) { 32 | return false; 33 | } 34 | return true; 35 | }; 36 | 37 | return list.map((item) => { 38 | return { 39 | value: item, 40 | label: item, 41 | selected: getInitial(item), 42 | }; 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/investigations/absent-boxes.js: -------------------------------------------------------------------------------- 1 | const { forEachList, getOtherBox } = require("@gavetaio/core"); 2 | 3 | export const investigateAbsentBoxes = ({ resultados, boxes, callback }) => { 4 | forEachList(boxes, (id, data) => { 5 | const { turno, scope, codigo, absolutos, municipio } = data; 6 | const { tamanho, aptos } = absolutos; 7 | 8 | if (!aptos) { 9 | return; 10 | } 11 | 12 | const presence = ((100 * tamanho) / aptos).toFixed(2); 13 | 14 | if (presence < 30) { 15 | const other = getOtherBox(data, boxes); 16 | 17 | const extra = { 18 | codigo, 19 | aptos, 20 | tamanho, 21 | municipio, 22 | }; 23 | 24 | if (other) { 25 | extra.other = { 26 | tamanho: other.absolutos.tamanho, 27 | }; 28 | } 29 | 30 | callback({ 31 | turno, 32 | scope, 33 | path: `boxes.faltantes`, 34 | push: { 35 | id, 36 | count: presence, 37 | extra, 38 | }, 39 | }); 40 | } 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/features/navigation.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { useCallback } from 'react'; 3 | import { 4 | useParams, 5 | useNavigate, 6 | useResolvedPath, 7 | useLocation, 8 | } from 'react-router-dom'; 9 | 10 | const scrollTop = () => { 11 | const el = document.querySelector('html'); 12 | el.scrollTo({ 13 | top: 0, 14 | }); 15 | }; 16 | 17 | export const useNavigation = () => { 18 | const { pathname: url } = useLocation(); 19 | 20 | const history = useNavigate(); 21 | 22 | const navigate = useCallback( 23 | (to, params = null) => { 24 | if (params?.event) { 25 | params.event.preventDefault(); 26 | } 27 | 28 | if (to.match(/^\//gim)) { 29 | scrollTop(); 30 | history(to); 31 | return; 32 | } 33 | scrollTop(); 34 | history(`${url}/${to}`); 35 | }, 36 | [history, url] 37 | ); 38 | 39 | const getRoute = useCallback(() => { 40 | return url; 41 | }, [url]); 42 | 43 | return { 44 | navigate, 45 | getRoute, 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /packages/ui/src/base/Section.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Section.module.scss"; 3 | 4 | const Section = ({ title, description, type = null, children = null }: any) => { 5 | const cls = [styles.container]; 6 | 7 | if (type) { 8 | cls.push(styles[type]); 9 | } 10 | 11 | if (type) { 12 | cls.push(styles[type]); 13 | } 14 | 15 | const renderTitle = () => { 16 | if (!title) { 17 | return null; 18 | } 19 | if (type === "main") { 20 | return

    {title}

    ; 21 | } 22 | if (type === "small") { 23 | return
    {title}
    ; 24 | } 25 | 26 | return

    {title}

    ; 27 | }; 28 | 29 | const renderedTitle = renderTitle(); 30 | 31 | return ( 32 |
    33 | {renderedTitle && ( 34 |
    {renderedTitle}
    35 | )} 36 |
    37 | {description &&
    {description}
    } 38 | {children} 39 |
    40 |
    41 | ); 42 | }; 43 | 44 | export default Section; 45 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Controls.tsx: -------------------------------------------------------------------------------- 1 | import Button from "./Button"; 2 | // @ts-ignore 3 | import styles from "./Controls.module.scss"; 4 | 5 | const Controls = ({ 6 | handleAction, 7 | onBlankPress, 8 | onBlankLongPress, 9 | onFixPress, 10 | onFixLongPress, 11 | onConfirmPress, 12 | onConfirmLongPress, 13 | }: any) => { 14 | return ( 15 |
    16 | 23 | 26 | 39 |
    40 | ); 41 | }; 42 | 43 | export default Controls; 44 | -------------------------------------------------------------------------------- /packages/elections/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'erb', 3 | rules: { 4 | 'import/no-extraneous-dependencies': 'off', 5 | 'react/react-in-jsx-scope': 'off', 6 | 'react/jsx-props-no-spreading': 'off', 7 | '@typescript-eslint/no-explicit-any': 'off', 8 | 'import/prefer-default-export': 'off', 9 | 'class-methods-use-this': 'off', 10 | '@typescript-eslint/ban-ts-comment': 'off', 11 | 'no-continue': 'off', 12 | 'react-hooks/exhaustive-deps': 'off', 13 | '@typescript-eslint/naming-convention': 'off', 14 | 'react/jsx-no-target-blank': 'off', 15 | }, 16 | parserOptions: { 17 | ecmaVersion: 2020, 18 | sourceType: 'module', 19 | project: './tsconfig.json', 20 | tsconfigRootDir: __dirname, 21 | createDefaultProgram: true, 22 | }, 23 | settings: { 24 | 'import/resolver': { 25 | node: {}, 26 | webpack: { 27 | config: require.resolve('./.erb/configs/webpack.config.eslint.ts'), 28 | }, 29 | }, 30 | 'import/parsers': { 31 | '@typescript-eslint/parser': ['.ts', '.tsx'], 32 | }, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/features/SmartPage.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { Page } from '@gavetaio/ui'; 3 | import { useEffect, useRef } from 'react'; 4 | import { useLocation, useNavigate } from 'react-router-dom'; 5 | import { useLayoutContext } from 'renderer/context/layout'; 6 | 7 | const SmartPage = ({ children }) => { 8 | const rendered = useRef(false); 9 | const location = useLocation(); 10 | const { saveLayout, getLayout }: any = useLayoutContext(); 11 | const navigate = useNavigate(); 12 | const layout = getLayout(); 13 | 14 | useEffect(() => { 15 | rendered.current = true; 16 | if (layout?.saved?.latest && layout.saved.latest !== location.pathname) { 17 | navigate(layout.saved.latest); 18 | } 19 | }, []); 20 | 21 | useEffect(() => { 22 | if (!rendered.current) { 23 | return; 24 | } 25 | if (layout?.saved?.latest && layout.saved.latest !== location.pathname) { 26 | saveLayout({ 27 | latest: location.pathname, 28 | }); 29 | } 30 | }, [location]); 31 | 32 | return {children}; 33 | }; 34 | 35 | export default SmartPage; 36 | -------------------------------------------------------------------------------- /packages/ui/src/base/Elevation.module.scss: -------------------------------------------------------------------------------- 1 | .content { 2 | position: relative; 3 | background-color: var(--elevation-bg-case); 4 | height: 100%; 5 | width: 100%; 6 | z-index: 4; 7 | border-radius: 4px; 8 | transform: translate3d( 9 | 0, 10 | calc(var(--elevation) * var(--elevation-state) * 1px), 11 | 0 12 | ); 13 | transition: transform 165ms cubic-bezier(0.5, 0.25, 0.05, 1); 14 | display: flex; 15 | align-items: center; 16 | justify-content: stretch; 17 | 18 | > * { 19 | position: relative; 20 | z-index: 2; 21 | } 22 | } 23 | 24 | .container { 25 | --elevation-bg-case: #333; 26 | --elevation-bg-dark: #000; 27 | --elevation: 7; 28 | --elevation-state: 0; 29 | 30 | display: block; 31 | position: absolute; 32 | width: 100%; 33 | height: 100%; 34 | top: 0; 35 | left: 0; 36 | 37 | &:before { 38 | content: ''; 39 | position: absolute; 40 | width: 100%; 41 | height: 100%; 42 | left: 0px; 43 | bottom: calc(var(--elevation) * -1px); 44 | z-index: 1; 45 | background-color: var(--elevation-bg-dark); 46 | border-radius: 4px; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Font.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Font.module.scss"; 3 | 4 | const Font = ({ 5 | children, 6 | name, 7 | type = "body", 8 | weight = "400", 9 | caps = false, 10 | bold = false, 11 | thin = false, 12 | }: any) => { 13 | const cls = [styles.font]; 14 | const style = { 15 | "--font-size": `var(--font-${name})`, 16 | "--font-weight": weight, 17 | } as React.CSSProperties; 18 | 19 | if (caps) { 20 | cls.push(styles.caps); 21 | } 22 | 23 | if (bold) { 24 | cls.push(styles.bold); 25 | } 26 | 27 | if (thin) { 28 | cls.push(styles.thin); 29 | } 30 | 31 | if (type === "body") { 32 | return ( 33 |

    34 | {children} 35 |

    36 | ); 37 | } 38 | 39 | if (type === "label") { 40 | return ( 41 | 44 | ); 45 | } 46 | 47 | return ( 48 |

    49 | {children} 50 |

    51 | ); 52 | }; 53 | 54 | export default Font; 55 | -------------------------------------------------------------------------------- /packages/ui/src/dre/legacy/Elevation.module.scss: -------------------------------------------------------------------------------- 1 | .content { 2 | position: relative; 3 | background-color: var(--elevation-bg-case); 4 | height: 100%; 5 | width: 100%; 6 | z-index: 4; 7 | border-radius: 4px; 8 | transform: translate3d( 9 | 0, 10 | calc(var(--elevation) * var(--elevation-state) * 1px), 11 | 0 12 | ); 13 | transition: transform 165ms cubic-bezier(0.5, 0.25, 0.05, 1); 14 | display: flex; 15 | align-items: center; 16 | justify-content: stretch; 17 | 18 | > * { 19 | position: relative; 20 | z-index: 2; 21 | } 22 | } 23 | 24 | .container { 25 | --elevation-bg-case: #333; 26 | --elevation-bg-dark: #000; 27 | --elevation: 7; 28 | --elevation-state: 0; 29 | 30 | display: block; 31 | position: absolute; 32 | width: 100%; 33 | height: 100%; 34 | top: 0; 35 | left: 0; 36 | 37 | &:before { 38 | content: ''; 39 | position: absolute; 40 | width: 100%; 41 | height: 100%; 42 | left: 0px; 43 | bottom: calc(var(--elevation) * -1px); 44 | z-index: 1; 45 | background-color: var(--elevation-bg-dark); 46 | border-radius: 4px; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/codex/src/integrity/interpretation/brazilian-electoral-code.ts: -------------------------------------------------------------------------------- 1 | import { ElectoralIntegrityData } from '../helpers/types'; 2 | import { 3 | NO_ACTION, 4 | PARTIAL_ELECTION_REPEAT, 5 | FULL_ELECTION_REPEAT, 6 | } from '../helpers/events'; 7 | 8 | class BrazilianElectoralCode { 9 | static invalidatedVotesThreshold = 50; 10 | 11 | shouldRepeatElection(electionData: ElectoralIntegrityData) { 12 | const { 13 | votes, 14 | canInvalidatedVotesChangeElectionResult, 15 | canPartialElectionRepeatRemedyElectionResult, 16 | } = electionData; 17 | 18 | if (!canInvalidatedVotesChangeElectionResult) { 19 | return NO_ACTION; 20 | } 21 | 22 | const invalidatedVotesPercentage = (100 * votes.invalidated) / votes.valid; 23 | 24 | if ( 25 | invalidatedVotesPercentage > 26 | BrazilianElectoralCode.invalidatedVotesThreshold 27 | ) { 28 | return FULL_ELECTION_REPEAT; 29 | } 30 | 31 | if (canPartialElectionRepeatRemedyElectionResult) { 32 | return PARTIAL_ELECTION_REPEAT; 33 | } 34 | 35 | return FULL_ELECTION_REPEAT; 36 | } 37 | } 38 | 39 | export default BrazilianElectoralCode; 40 | -------------------------------------------------------------------------------- /packages/elections/.erb/configs/webpack.config.base.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Base webpack config used across other specific configs 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import webpackPaths from './webpack.paths'; 7 | import { dependencies as externals } from '../../release/app/package.json'; 8 | 9 | export default { 10 | externals: [...Object.keys(externals || {})], 11 | 12 | stats: 'errors-only', 13 | 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.[jt]sx?$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'ts-loader', 21 | }, 22 | }, 23 | ], 24 | }, 25 | 26 | output: { 27 | path: webpackPaths.srcPath, 28 | // https://github.com/webpack/webpack/issues/1114 29 | library: { 30 | type: 'commonjs2', 31 | }, 32 | }, 33 | 34 | /** 35 | * Determine the array of extensions that should be used to resolve modules. 36 | */ 37 | resolve: { 38 | extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], 39 | modules: [webpackPaths.srcPath, 'node_modules'], 40 | }, 41 | 42 | plugins: [ 43 | new webpack.EnvironmentPlugin({ 44 | NODE_ENV: 'production', 45 | }), 46 | ], 47 | }; 48 | -------------------------------------------------------------------------------- /packages/elections/.erb/configs/webpack.paths.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.join(__dirname, '../..'); 4 | 5 | const dllPath = path.join(__dirname, '../dll'); 6 | 7 | const srcPath = path.join(rootPath, 'src'); 8 | const srcMainPath = path.join(srcPath, 'main'); 9 | const srcRendererPath = path.join(srcPath, 'renderer'); 10 | 11 | const releasePath = path.join(rootPath, 'release'); 12 | const appPath = path.join(releasePath, 'app'); 13 | const appPackagePath = path.join(appPath, 'package.json'); 14 | const appNodeModulesPath = path.join(appPath, 'node_modules'); 15 | const srcNodeModulesPath = path.join(srcPath, 'node_modules'); 16 | 17 | const distPath = path.join(appPath, 'dist'); 18 | const distMainPath = path.join(distPath, 'main'); 19 | const distRendererPath = path.join(distPath, 'renderer'); 20 | 21 | const buildPath = path.join(releasePath, 'build'); 22 | 23 | export default { 24 | rootPath, 25 | dllPath, 26 | srcPath, 27 | srcMainPath, 28 | srcRendererPath, 29 | releasePath, 30 | appPath, 31 | appPackagePath, 32 | appNodeModulesPath, 33 | srcNodeModulesPath, 34 | distPath, 35 | distMainPath, 36 | distRendererPath, 37 | buildPath, 38 | }; 39 | -------------------------------------------------------------------------------- /packages/core/utils/transformers/table.js: -------------------------------------------------------------------------------- 1 | import { forEachList, getPercentageString } from '../general'; 2 | 3 | export const getTableCell = ({ 4 | label = null, 5 | value = 0, 6 | extra = null, 7 | connected = false, 8 | } = {}) => { 9 | return { 10 | label: label || value || '—', 11 | value, 12 | extra, 13 | connected, 14 | }; 15 | }; 16 | 17 | export const getInfoCell = (label, votes, total, type = null) => { 18 | return { 19 | type, 20 | data: [ 21 | label, 22 | { 23 | value: votes, 24 | extra: getPercentageString(votes, total), 25 | }, 26 | ], 27 | }; 28 | }; 29 | 30 | export const createTableCellObject = (obj) => { 31 | const cell = {}; 32 | forEachList(obj, (key, value) => { 33 | if (typeof value === 'object') { 34 | cell[key] = getTableCell(value); 35 | return; 36 | } 37 | cell[key] = getTableCell({ value }); 38 | }); 39 | return cell; 40 | }; 41 | 42 | export const objectToTable = (obj) => { 43 | const keys = Object.keys(obj); 44 | const result = []; 45 | for (let i = 0; i < keys.length; i += 1) { 46 | const key = keys[i]; 47 | result.push(obj[key]); 48 | } 49 | return result; 50 | }; 51 | -------------------------------------------------------------------------------- /packages/ui/src/base/ListItem.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/destructuring-assignment */ 2 | // @ts-ignore 3 | import styles from "./ListItem.module.scss"; 4 | import Table from "./Table"; 5 | import Tags from "./Tags"; 6 | 7 | const ListItem = ({ 8 | title, 9 | description, 10 | table, 11 | action, 12 | tags, 13 | href = "", 14 | }: any) => { 15 | const content = ( 16 |
    17 |
    18 |

    {title}

    19 | {description} 20 |
    21 | {tags?.length && ( 22 |
    23 | 24 |
    25 | )} 26 | {table && (table.length || table.data?.length) && ( 27 |
    28 | 29 | 30 | )} 31 | 32 | ); 33 | 34 | if (action) { 35 | return ( 36 | { 38 | event.preventDefault(); 39 | action(href); 40 | }} 41 | href={href} 42 | > 43 | {content} 44 | 45 | ); 46 | } 47 | 48 | return content; 49 | }; 50 | 51 | export default ListItem; 52 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/SimulatorSummary.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import { forEachList } from "@gavetaio/core"; 3 | import { Table } from "../../base"; 4 | 5 | const infoToSimulatorTable: any = ({ config, extra }) => { 6 | const header = ["Tipo", "Valor"]; 7 | const data = []; 8 | 9 | const { candidatos, partidos, settings } = config; 10 | 11 | forEachList(extra, (key, value) => { 12 | data.push([key, value]); 13 | }); 14 | 15 | data.push(["candidatos", candidatos?.length || 0]); 16 | data.push(["partidos", partidos?.length || 0]); 17 | data.push(["votos", settings?.length || 0]); 18 | 19 | const cargoList = []; 20 | settings.forEach(({ cargo }) => { 21 | if (cargoList.indexOf(cargo) === -1) { 22 | cargoList.push(cargo); 23 | } 24 | }); 25 | 26 | data.push(["cargos", cargoList.join(", ")]); 27 | 28 | settings.forEach(() => { 29 | // 30 | }); 31 | 32 | return { 33 | header, 34 | data, 35 | }; 36 | }; 37 | 38 | const SimulatorSummary: any = ({ config, extra }: any) => { 39 | const data: any = infoToSimulatorTable({ 40 | config, 41 | extra, 42 | }); 43 | 44 | return
    ; 45 | }; 46 | 47 | export default SimulatorSummary; 48 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/SimulatorVotes.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import { forEachList } from "@gavetaio/core"; 3 | import { Table } from "../../base"; 4 | 5 | const getState = (number) => { 6 | if (number === "96") { 7 | return "nulo"; 8 | } 9 | if (number === "95") { 10 | return "branco"; 11 | } 12 | return "válido"; 13 | }; 14 | 15 | const getCargoDisplay = (id, settings) => { 16 | const item = settings.find((setting) => setting.id === id); 17 | return item?.label || null; 18 | }; 19 | 20 | const votesToTable: any = ({ votes, settings }) => { 21 | const header = ["Cargo", "Número Digitado", "Número Gravado", "Status"]; 22 | const data = []; 23 | 24 | forEachList(votes, (key, { digitado, gravado }) => { 25 | data.push([ 26 | getCargoDisplay(key, settings), 27 | digitado, 28 | gravado, 29 | getState(gravado), 30 | ]); 31 | }); 32 | 33 | return { 34 | header, 35 | data, 36 | }; 37 | }; 38 | 39 | const SimulatorVotes: any = ({ votes, config }: any) => { 40 | const { settings } = config; 41 | 42 | const data: any = votesToTable({ votes, settings }); 43 | 44 | return
    ; 45 | }; 46 | 47 | export default SimulatorVotes; 48 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/helpers.tsx: -------------------------------------------------------------------------------- 1 | export const getPartidoFromList = ({ numero, list }) => { 2 | const test = typeof numero === "number" ? `${numero}` : numero; 3 | 4 | if (!test) { 5 | return null; 6 | } 7 | return list.find((partido) => partido.numero === test); 8 | }; 9 | 10 | export const genderFlip = (nome) => { 11 | const split = nome.split(" "); 12 | if (split[0].trim().match(/(or)$/gim)) { 13 | split[0] = split[0].replace(/(.*)(or)$/gim, "$1ora"); 14 | } 15 | 16 | if (split[0].trim().match(/(o)$/gim)) { 17 | split[0] = split[0].replace(/(.*)(o)$/gim, "$1a"); 18 | } 19 | 20 | return split.join(" "); 21 | }; 22 | 23 | export const getCargoDisplayTitle = ({ current, state }) => { 24 | const title = current?.label; 25 | if (state?.candidato?.genero && state?.candidato?.genero !== 2) { 26 | return genderFlip(title); 27 | } 28 | 29 | return title; 30 | }; 31 | 32 | export const getTransformedNumero = (status, numbers) => { 33 | const { isNulo, isBranco, isLegenda, hasCandidato } = status; 34 | 35 | if (isBranco) { 36 | return "95"; 37 | } 38 | 39 | if (isNulo) { 40 | return "96"; 41 | } 42 | 43 | if (isLegenda && !hasCandidato) { 44 | return numbers.slice(0, 2).join(""); 45 | } 46 | 47 | return numbers.join(""); 48 | }; 49 | -------------------------------------------------------------------------------- /packages/ui/src/base/Block.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Block.module.scss"; 3 | 4 | const Block = ({ 5 | title, 6 | description, 7 | children, 8 | noMargin = false, 9 | marginDouble = false, 10 | noPadding = false, 11 | halfMargin = false, 12 | lighter = false, 13 | footer = null, 14 | darker = false, 15 | type = null, 16 | }: any) => { 17 | const cls = [styles.container]; 18 | 19 | if (noMargin) { 20 | cls.push(styles.noMargin); 21 | } 22 | 23 | if (marginDouble) { 24 | cls.push(styles.marginDouble); 25 | } 26 | 27 | if (halfMargin) { 28 | cls.push(styles.halfMargin); 29 | } 30 | 31 | if (noPadding) { 32 | cls.push(styles.noPadding); 33 | } 34 | 35 | if (lighter) { 36 | cls.push(styles.lighter); 37 | } 38 | 39 | if (darker) { 40 | cls.push(styles.darker); 41 | } 42 | 43 | if (type && styles[type]) { 44 | cls.push(styles[type]); 45 | } 46 | 47 | return ( 48 |
    49 |
    50 | {description} 51 | {title &&
    {title}
    } 52 |
    53 |
    {children}
    54 | {footer &&
    {footer}
    } 55 |
    56 | ); 57 | }; 58 | 59 | export default Block; 60 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/App.tsx: -------------------------------------------------------------------------------- 1 | import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; 2 | import './App.global.css'; 3 | import './Reset.module.scss'; 4 | import { LayoutProvider } from './context/layout'; 5 | import LoaderScreen from './screens/Loader'; 6 | import { 7 | SimuladorPage, 8 | HomePage, 9 | NullBoxesPage, 10 | ExposedPage, 11 | NulledVotesPage, 12 | AboutPage, 13 | } from './screens'; 14 | import Navigation from './components/Navigation'; 15 | import SmartPage from './features/SmartPage'; 16 | import Bar from './components/Bar'; 17 | 18 | export default function App() { 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | } /> 27 | } /> 28 | } /> 29 | } /> 30 | } /> 31 | } /> 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /packages/ui/src/dre/legacy/Button.module.old.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | --button-default-height: 42px; 3 | --button-horizontal-padding: calc( 4 | var(--button-default-height) - var(--button-raise-level) 5 | ); 6 | 7 | &.left { 8 | > span > span { 9 | padding-right: calc(var(--button-horizontal-padding)); 10 | padding-left: calc(var(--button-horizontal-padding) * 3 / 2); 11 | } 12 | .content { 13 | > span:nth-child(2) { 14 | left: 0; 15 | padding-right: 0; 16 | } 17 | } 18 | } 19 | 20 | &.right { 21 | > span > span { 22 | padding-left: calc(var(--button-horizontal-padding)); 23 | padding-right: calc(var(--button-horizontal-padding) * 3 / 2); 24 | } 25 | .content { 26 | > span:nth-child(2) { 27 | right: 0; 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | } 33 | 34 | .content { 35 | display: block; 36 | font-size: 13px; 37 | line-height: 25; 38 | 39 | > span:nth-child(1) { 40 | display: block; 41 | font-size: inherit; 42 | } 43 | 44 | > span:nth-child(2) { 45 | padding-right: calc(var(--button-horizontal-padding)); 46 | padding-left: calc(var(--button-horizontal-padding)); 47 | height: var(--button-horizontal-padding); 48 | display: flex; 49 | position: absolute; 50 | top: 0; 51 | align-items: center; 52 | justify-content: center; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/ui/src/dre/revisited/Button.module.old.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | --button-default-height: 42px; 3 | --button-horizontal-padding: calc( 4 | var(--button-default-height) - var(--button-raise-level) 5 | ); 6 | 7 | &.left { 8 | > span > span { 9 | padding-right: calc(var(--button-horizontal-padding)); 10 | padding-left: calc(var(--button-horizontal-padding) * 3 / 2); 11 | } 12 | .content { 13 | > span:nth-child(2) { 14 | left: 0; 15 | padding-right: 0; 16 | } 17 | } 18 | } 19 | 20 | &.right { 21 | > span > span { 22 | padding-left: calc(var(--button-horizontal-padding)); 23 | padding-right: calc(var(--button-horizontal-padding) * 3 / 2); 24 | } 25 | .content { 26 | > span:nth-child(2) { 27 | right: 0; 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | } 33 | 34 | .content { 35 | display: block; 36 | font-size: 13px; 37 | line-height: 25; 38 | 39 | > span:nth-child(1) { 40 | display: block; 41 | font-size: inherit; 42 | } 43 | 44 | > span:nth-child(2) { 45 | padding-right: calc(var(--button-horizontal-padding)); 46 | padding-left: calc(var(--button-horizontal-padding)); 47 | height: var(--button-horizontal-padding); 48 | display: flex; 49 | position: absolute; 50 | top: 0; 51 | align-items: center; 52 | justify-content: center; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/codex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gavetaio/codex", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "scripts": { 7 | "start": "ts-node ./src/index.ts", 8 | "test": "jest --noStackTrace", 9 | "lint": "eslint src public" 10 | }, 11 | "author": "@gavetaio", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@types/jest": "^29.0.2", 15 | "@types/node": "^18.7.16", 16 | "@typescript-eslint/eslint-plugin": "^5.36.2", 17 | "@typescript-eslint/parser": "^5.36.2", 18 | "eslint": "^8.23.0", 19 | "eslint-config-prettier": "^8.5.0", 20 | "eslint-plugin-node": "^11.1.0", 21 | "jest": "^29.0.3", 22 | "prettier": "^2.7.1", 23 | "ts-node": "^10.9.1", 24 | "tslib": "^2.4.0", 25 | "typescript": "^4.8.2" 26 | }, 27 | "jest": { 28 | "transform": { 29 | ".(ts|tsx)": "ts-jest" 30 | }, 31 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 32 | "moduleFileExtensions": [ 33 | "ts", 34 | "tsx", 35 | "js", 36 | "json" 37 | ] 38 | }, 39 | "prettier": { 40 | "overrides": [ 41 | { 42 | "files": [ 43 | ".prettierrc", 44 | ".eslintrc" 45 | ], 46 | "options": { 47 | "parser": "json" 48 | } 49 | } 50 | ], 51 | "singleQuote": true 52 | }, 53 | "dependencies": { 54 | "ts-jest": "^29.0.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/components/Navigation.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: var(--background-darker); 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | position: fixed; 7 | width: var(--menu-width); 8 | height: 100vh; 9 | z-index: 1000; 10 | top: 0; 11 | left: 0; 12 | overflow: hidden; 13 | 14 | .wrapper { 15 | padding: 16px; 16 | width: 100%; 17 | overflow: auto; 18 | background-color: var(--background-darker); 19 | 20 | a { 21 | display: block; 22 | cursor: default; 23 | color: var(--text-primary); 24 | padding: 8px 20px; 25 | border-radius: 32px; 26 | margin: 8px 0; 27 | background-color: var(--background-dark); 28 | } 29 | } 30 | 31 | header { 32 | display: flex; 33 | width: 100%; 34 | 35 | justify-content: flex-end; 36 | align-items: center; 37 | min-height: var(--bar-height); 38 | padding: 0 16px; 39 | 40 | h2 { 41 | letter-spacing: -1.5px; 42 | color: var(--text-primary); 43 | } 44 | } 45 | 46 | section { 47 | margin-bottom: 24px; 48 | } 49 | 50 | h6 { 51 | padding-left: 20px; 52 | margin-bottom: 4px; 53 | text-transform: uppercase; 54 | color: var(--text-alt); 55 | } 56 | 57 | .active { 58 | background-color: var(--background); 59 | h5 { 60 | color: var(--text-secondary); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Button.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .button { 2 | --button-width: 29; 3 | --button-height: 12; 4 | --button-background: #ffffff; 5 | --button-background-dark: #ffffff; 6 | 7 | position: relative; 8 | width: calc(var(--unit) * var(--button-width)); 9 | height: calc(var(--unit) * var(--button-height)); 10 | margin: 0 calc(var(--key-height) / 4); 11 | 12 | > div { 13 | --elevation-bg-dark: var(--button-background-dark) !important; 14 | --elevation-bg-case: var(--button-background) !important; 15 | } 16 | 17 | p { 18 | color: var(--black); 19 | text-align: center; 20 | width: 100%; 21 | align-self: flex-start; 22 | margin-top: calc(var(--unit) / 2); 23 | } 24 | 25 | &.white { 26 | --button-background: var(--button-white); 27 | --button-background-dark: var(--button-white-dark); 28 | 29 | p { 30 | transform: scaleX(1) scaleY(1.1); 31 | } 32 | } 33 | 34 | &.danger { 35 | --button-background: var(--button-danger); 36 | --button-background-dark: var(--button-danger-dark); 37 | display: block; 38 | 39 | p { 40 | transform: scaleX(0.92) scaleY(1.1); 41 | } 42 | } 43 | 44 | &.action { 45 | --button-background: var(--button-action); 46 | --button-background-dark: var(--button-action-dark); 47 | --button-height: 16; 48 | 49 | p { 50 | transform: scaleX(0.86) scaleY(1.1); 51 | transform-origin: 50% 10%; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/core/utils/transformers/filters.js: -------------------------------------------------------------------------------- 1 | const compareStatic = (list, item) => { 2 | if (list.indexOf(item) === -1) { 3 | return false; 4 | } 5 | return true; 6 | }; 7 | 8 | const compareArrays = (list, items) => { 9 | let result = false; 10 | list.forEach((listItem) => { 11 | items.forEach((item) => { 12 | if (item === listItem) { 13 | result = true; 14 | } 15 | }); 16 | }); 17 | return result; 18 | }; 19 | 20 | export const applyFilter = ({ 21 | filters, 22 | ano = null, 23 | cargo = null, 24 | uf = null, 25 | anos = null, 26 | cargos = null, 27 | ufs = null, 28 | }) => { 29 | if (ano && filters.years?.length && !compareStatic(filters.years, ano)) { 30 | return false; 31 | } 32 | 33 | if ( 34 | cargo && 35 | filters.cargos?.length && 36 | !compareStatic(filters.cargos, cargo) 37 | ) { 38 | return false; 39 | } 40 | 41 | if (uf && filters.ufs?.length && !compareStatic(filters.ufs, uf)) { 42 | return false; 43 | } 44 | 45 | if ( 46 | anos?.length && 47 | filters.years?.length && 48 | !compareArrays(filters.years, anos) 49 | ) { 50 | return false; 51 | } 52 | 53 | if ( 54 | cargos?.length && 55 | filters.cargos?.length && 56 | !compareArrays(filters.cargos, cargos) 57 | ) { 58 | return false; 59 | } 60 | 61 | if (ufs?.length && filters.ufs?.length && !compareArrays(filters.ufs, ufs)) { 62 | return false; 63 | } 64 | 65 | return true; 66 | }; 67 | -------------------------------------------------------------------------------- /packages/ui/src/base/helpers.js: -------------------------------------------------------------------------------- 1 | function end(element, type, { tolerance = 0, propertyName } = {}) { 2 | return new Promise((resolve) => { 3 | if (!element) { 4 | resolve(false); 5 | return; 6 | } 7 | let eventName = null; 8 | const capitalized = type.charAt(0).toUpperCase() + type.slice(1); 9 | let run = 0; 10 | function end(event) { 11 | const target = event.srcElement || event.target; 12 | if (target === element) { 13 | if (run >= tolerance) { 14 | if (propertyName && propertyName !== event.propertyName) { 15 | return; 16 | } 17 | element.removeEventListener(eventName, end); 18 | resolve(event); 19 | } 20 | run += 1; 21 | } 22 | } 23 | if (element.style[`Webkit${capitalized}`] !== undefined) { 24 | eventName = `webkit${capitalized}End`; 25 | } 26 | if (element.style.OTransition !== undefined) { 27 | eventName = `o${type}End`; 28 | } 29 | if (element.style[type] !== undefined) { 30 | eventName = `${type}end`; 31 | } 32 | if (element.clearEvent) { 33 | element.clearEvent(); 34 | } 35 | element.clearEvent = function () { 36 | element.removeEventListener(eventName, end); 37 | }; 38 | element.addEventListener(eventName, end); 39 | }); 40 | } 41 | 42 | export function onTransitionEnd(element, options = {}) { 43 | return new Promise((resolve) => { 44 | end(element, "transition", options).then(resolve); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/NulledVotesPage/transformers.ts: -------------------------------------------------------------------------------- 1 | import { 2 | forEachList, 3 | objectToTable, 4 | createTableCellObject, 5 | } from '@gavetaio/core'; 6 | import { chartObjectToData } from 'renderer/helpers/data'; 7 | 8 | export const filteredNulledSummary = ({ byYear }) => { 9 | const list = byYear; 10 | 11 | const header = ['Ano / Turno', 'Eleitores', 'Votos', 'Excluídos']; 12 | const result = []; 13 | const groups = {}; 14 | 15 | const chart: any = { 16 | federal: {}, 17 | municipal: {}, 18 | }; 19 | 20 | for (let i = 0; i < list.length; i += 1) { 21 | const item = list[i]; 22 | 23 | const { turno, id, comparecimento, votos, recursados, isFederal } = item; 24 | 25 | const chartName = isFederal ? 'federal' : 'municipal'; 26 | 27 | const key = `${id}-${turno}`; 28 | 29 | if (!chart[chartName][id]) { 30 | chart[chartName][id] = { 31 | count: 0, 32 | total: 0, 33 | }; 34 | } 35 | 36 | chart[chartName][id].count += recursados; 37 | 38 | const cell = createTableCellObject({ 39 | id: key, 40 | comparecimento, 41 | votos, 42 | removidos: { 43 | value: recursados, 44 | connected: true, 45 | }, 46 | }); 47 | 48 | result.push(objectToTable(cell)); 49 | } 50 | 51 | return { 52 | table: { 53 | header, 54 | firstRow: 12, 55 | title: 'Dados', 56 | data: result, 57 | footer: ['count', 'sum', null, 'sum'], 58 | }, 59 | 60 | chart: chartObjectToData(chart), 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /packages/codex/src/integrity/__tests__/interpretation.ts: -------------------------------------------------------------------------------- 1 | import BrazilianElectoralCode from '../interpretation/brazilian-electoral-code'; 2 | import BrazilianElectoralCourt from '../interpretation/brazilian-electoral-court'; 3 | import WesternElectoralHeritage from '../interpretation/western-electoral-heritage'; 4 | import data from '../data/brazilian-election-2018'; 5 | 6 | const brazilianElectoralCode = new BrazilianElectoralCode(); 7 | const brazilianElectoralCourt = new BrazilianElectoralCourt(); 8 | const westernElectoralHeritage = new WesternElectoralHeritage(); 9 | 10 | describe('Brazilian Electoral CODE interpretation', () => { 11 | data.forEach((election, index) => { 12 | const brazilianCodeEvent = 13 | brazilianElectoralCode.shouldRepeatElection(election); 14 | const westernHeritageEvent = 15 | westernElectoralHeritage.shouldRepeatElection(election); 16 | 17 | test(`T${index}: repeat event should match WesternElectoralHeritage`, () => { 18 | expect(brazilianCodeEvent).toBe(westernHeritageEvent); 19 | }); 20 | }); 21 | }); 22 | 23 | describe('Brazilian Electoral COURT interpretation', () => { 24 | data.forEach((election, index) => { 25 | const brazilianCourtEvent = 26 | brazilianElectoralCourt.shouldRepeatElection(election); 27 | const westernHeritageEvent = 28 | westernElectoralHeritage.shouldRepeatElection(election); 29 | 30 | test(`T${index}: ${election.name}: repeat event should match WesternElectoralHeritage`, () => { 31 | expect(brazilianCourtEvent).toBe(westernHeritageEvent); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/codex/src/integrity/interpretation/brazilian-electoral-court.ts: -------------------------------------------------------------------------------- 1 | import { ElectoralIntegrityData } from '../helpers/types'; 2 | import { 3 | NO_ACTION, 4 | // PARTIAL_ELECTION_REPEAT, 5 | FULL_ELECTION_REPEAT, 6 | } from '../helpers/events'; 7 | 8 | class BrazilianElectoralCodeCourtInterpretation { 9 | static INVALIDATED_VOTES_THRESHOLD = 50; 10 | 11 | shouldRepeatElection(electionData: ElectoralIntegrityData) { 12 | const { 13 | votes, 14 | // canInvalidatedVotesChangeElectionResult, 15 | canPartialElectionRepeatRemedyElectionResult, 16 | invalidatedPollingStations, 17 | wasMajoritarianElectedCandidateInvalidated = null, 18 | } = electionData; 19 | 20 | // if (!canInvalidatedVotesChangeElectionResult) { 21 | // return NO_ACTION; 22 | // } 23 | 24 | const invalidatedVotesPercentage = (100 * votes.invalidated) / votes.valid; 25 | 26 | if ( 27 | invalidatedVotesPercentage > 28 | BrazilianElectoralCodeCourtInterpretation.INVALIDATED_VOTES_THRESHOLD 29 | ) { 30 | return FULL_ELECTION_REPEAT; 31 | } 32 | 33 | if (wasMajoritarianElectedCandidateInvalidated === true) { 34 | return FULL_ELECTION_REPEAT; 35 | } 36 | 37 | if (canPartialElectionRepeatRemedyElectionResult) { 38 | // return PARTIAL_ELECTION_REPEAT; 39 | if (invalidatedPollingStations === 1) { 40 | return NO_ACTION; 41 | } 42 | 43 | return NO_ACTION; 44 | } 45 | 46 | // return FULL_ELECTION_REPEAT; 47 | return NO_ACTION; 48 | } 49 | } 50 | 51 | export default BrazilianElectoralCodeCourtInterpretation; 52 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Font.module.scss: -------------------------------------------------------------------------------- 1 | :global(#sim-urna) .font { 2 | --font-size: 1; 3 | --line-height: 1.35; 4 | --font-weight: 500; 5 | 6 | font-family: var(--dre-font-family); 7 | font-size: calc(var(--font-body) * var(--font-size)); 8 | line-height: calc(var(--font-body) * var(--font-size) * var(--line-height)); 9 | font-weight: var(--font-weight); 10 | min-height: calc(var(--font-body) * var(--font-size) * var(--line-height)); 11 | position: relative; 12 | margin-bottom: 0 !important; 13 | padding: 0; 14 | 15 | &.caps { 16 | text-transform: uppercase; 17 | } 18 | 19 | &.bold { 20 | font-weight: 600; 21 | } 22 | 23 | &.thin { 24 | font-weight: 300; 25 | } 26 | 27 | b, 28 | i, 29 | span, 30 | mark, 31 | em { 32 | color: inherit; 33 | font-size: inherit; 34 | } 35 | 36 | mark { 37 | background-color: var(--black); 38 | color: var(--white); 39 | display: inline-flex; 40 | padding: var(--unit); 41 | align-items: center; 42 | justify-content: center; 43 | min-height: calc(var(--unit) * 7); 44 | min-height: calc(var(--unit) * 7); 45 | font-weight: 500; 46 | border-radius: calc(var(--unit) / 2); 47 | 48 | span { 49 | display: block; 50 | font-weight: 600; 51 | } 52 | } 53 | 54 | mark[data-type="confirm"] { 55 | background-color: var(--button-action); 56 | color: var(--black); 57 | min-height: calc(var(--unit) * 5); 58 | } 59 | 60 | mark[data-type="danger"] { 61 | background-color: var(--button-danger); 62 | color: var(--black); 63 | min-height: calc(var(--unit) * 5); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gavetaio/core", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "module": "./index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "lint": "eslint src public" 10 | }, 11 | "author": "@gavetaio", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "eslint": "^8.6.0", 15 | "eslint-config-airbnb-base": "^15.0.0", 16 | "eslint-config-erb": "^4.0.0-alpha.0", 17 | "eslint-config-prettier": "^8.3.0", 18 | "eslint-plugin-compat": "^4.0.1", 19 | "eslint-plugin-import": "^2.25.4", 20 | "eslint-plugin-jsx-a11y": "^6.5.1", 21 | "eslint-plugin-promise": "^6.0.0", 22 | "eslint-plugin-react": "^7.28.0", 23 | "eslint-plugin-react-hooks": "^4.3.0", 24 | "prettier": "^2.5.1", 25 | "typescript-plugin-css-modules": "^3.4.0" 26 | }, 27 | "prettier": { 28 | "overrides": [ 29 | { 30 | "files": [ 31 | ".prettierrc", 32 | ".eslintrc" 33 | ], 34 | "options": { 35 | "parser": "json" 36 | } 37 | } 38 | ], 39 | "singleQuote": true 40 | }, 41 | "lint-staged": { 42 | "*.{js,jsx,ts,tsx}": [ 43 | "cross-env NODE_ENV=development eslint --cache" 44 | ], 45 | "*.json,.{eslintrc,prettierrc}": [ 46 | "prettier --ignore-path .eslintignore --parser json --write" 47 | ], 48 | "*.{css,scss}": [ 49 | "prettier --ignore-path .eslintignore --single-quote --write" 50 | ], 51 | "*.{html,md,yml}": [ 52 | "prettier --ignore-path .eslintignore --single-quote --write" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/investigations/index.js: -------------------------------------------------------------------------------- 1 | const { investigateSidedVotes } = require("./sided-vote"); 2 | const { investigateNullBoxes } = require("./null-boxes"); 3 | const { investigateRemovedVotes } = require("./removed-votes"); 4 | const { investigateBoxVoting } = require("./box-voting"); 5 | const { investigateIndeferidos } = require("./indeferidos"); 6 | const { investigateMissingVotes } = require("./missing-votes"); 7 | const { investigateSizeDeviation } = require("./size-deviation"); 8 | const { investigateAbsentBoxes } = require("./absent-boxes"); 9 | const { investigateMissingBoxes } = require("./missing-boxes"); 10 | const { investigateColigVotes } = require("./colig-votes"); 11 | const { investigateMissingPositions } = require("./missing-positions"); 12 | const { investigateTransitionBoxes } = require("./transition-boxes"); 13 | const { investigateVoteDeviation } = require("./vote-deviation"); 14 | const { populateCityInfo } = require("./city-info"); 15 | 16 | module.exports = (investigationData) => { 17 | investigateSidedVotes(investigationData); 18 | investigateMissingVotes(investigationData); 19 | investigateMissingPositions(investigationData); 20 | investigateTransitionBoxes(investigationData); 21 | investigateNullBoxes(investigationData); 22 | investigateSizeDeviation(investigationData); 23 | investigateVoteDeviation(investigationData); 24 | investigateAbsentBoxes(investigationData); 25 | investigateMissingBoxes(investigationData); 26 | investigateColigVotes(investigationData); 27 | investigateRemovedVotes(investigationData); 28 | populateCityInfo(investigationData); 29 | investigateBoxVoting(investigationData); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/ui/src/base/Logo.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | --logo-size: 74px; 3 | --logo-border-radius: calc(var(--logo-size) / 32); 4 | --logo-handler-bg: #fff; 5 | 6 | --logo-bg: #fff; 7 | --logo-bg-secondary: #fff; 8 | --logo-spacer: calc(1px * 2); 9 | 10 | display: flex; 11 | height: var(--logo-size); 12 | width: var(--logo-size); 13 | position: relative; 14 | 15 | > div { 16 | display: flex; 17 | flex-direction: column; 18 | justify-content: space-between; 19 | position: relative; 20 | flex: 1; 21 | } 22 | 23 | &.circled { 24 | background-color: var(--logo-bg); 25 | border-radius: 50%; 26 | 27 | > div { 28 | margin: calc(var(--logo-size) / 4); 29 | } 30 | 31 | span { 32 | background-color: var(--logo-bg-secondary); 33 | } 34 | } 35 | 36 | span { 37 | display: block; 38 | position: relative; 39 | height: calc((100% - var(--logo-spacer)) / 3); 40 | background-color: var(--logo-bg); 41 | border-radius: var(--logo-border-radius); 42 | clip-path: polygon( 43 | 0% 0%, 44 | 0% 100%, 45 | 40% 100%, 46 | 40% 40%, 47 | 60% 40%, 48 | 60% 60%, 49 | 40% 60%, 50 | 40% 100%, 51 | 100% 100%, 52 | 100% 0% 53 | ); 54 | } 55 | 56 | &.noclip { 57 | span { 58 | clip-path: none; 59 | } 60 | 61 | span:before { 62 | content: ""; 63 | position: absolute; 64 | width: 25%; 65 | height: 20%; 66 | top: 40%; 67 | left: calc(50% - 12.5%); 68 | border-radius: var(--logo-border-radius); 69 | background-color: var(--logo-handler-bg); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Box.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | --unit: 3px; 3 | --unit-m: calc(var(--unit) * 4); 4 | --black: #111111; 5 | --keypad: #444444; 6 | --grey: #cfcfcf; 7 | --grey-dark: #aaaaaa; 8 | --border: #aaaaaa; 9 | --white: #ffffff; 10 | --ice: #f4f4f4; 11 | --screen: #efefef; 12 | --color-key: #333; 13 | --dark-grey: #3a3a3a; 14 | --lighthen-10: rgba(255, 255, 255, 0.5); 15 | --screen-ratio: 0.6; 16 | --screen-base: 180; 17 | --screen-width: calc(var(--unit) * var(--screen-base)); 18 | --screen-height: calc(var(--screen-width) * var(--screen-ratio)); 19 | --keypad-width: calc(var(--screen-width) * 0.6); 20 | --key-ratio: calc(228 / 180); 21 | --key-width: calc(var(--unit) * 18); 22 | --key-height: calc(var(--key-width) / var(--key-ratio)); 23 | --button-white: #fafbff; 24 | --button-white-dark: #b9b9b9; 25 | --button-danger: #f59a62; 26 | --button-danger-dark: #b46534; 27 | --button-action: #51d9a1; 28 | --button-action-dark: #268d62; 29 | 30 | padding: calc(var(--unit) * 5); 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | 35 | @media all and (max-width: 1360px) { 36 | --unit: 2.5px; 37 | } 38 | 39 | @media all and (max-width: 1190px) { 40 | --unit: 2px; 41 | } 42 | 43 | @media all and (max-width: 1020px) { 44 | --unit: 1.8px; 45 | } 46 | 47 | > div { 48 | background-color: var(--grey); 49 | display: block; 50 | border-radius: calc(var(--unit) / 2); 51 | } 52 | } 53 | 54 | .box { 55 | padding: calc(var(--unit) * 10) calc(var(--unit) * 16); 56 | display: flex; 57 | align-items: center; 58 | position: relative; 59 | } 60 | -------------------------------------------------------------------------------- /packages/ui/src/base/Selector.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: row; 4 | align-items: center; 5 | justify-content: flex-start; 6 | padding: var(--unit-v-0) 0; 7 | flex-wrap: wrap; 8 | width: 100%; 9 | border-radius: var(--unit-v-0); 10 | background-color: var(--background-dark); 11 | 12 | > div { 13 | display: flex; 14 | flex-direction: row; 15 | align-items: center; 16 | justify-content: flex-start; 17 | width: 100%; 18 | flex-wrap: wrap; 19 | 20 | > :nth-child(1) { 21 | margin-left: 0; 22 | } 23 | } 24 | 25 | button { 26 | display: flex; 27 | padding: 12px; 28 | margin: 0 4px 4px 0; 29 | border-radius: 4px; 30 | background-color: var(--background-dark); 31 | color: var(--text-lighter); 32 | align-items: center; 33 | 34 | > svg { 35 | margin: 0px 0 0 2px; 36 | fill: var(--text-lighter); 37 | } 38 | } 39 | 40 | button.selected { 41 | background-color: var(--background-light); 42 | color: var(--text-alt); 43 | 44 | span { 45 | color: var(--text-alt); 46 | } 47 | 48 | svg { 49 | fill: var(--text-alt); 50 | } 51 | } 52 | 53 | &.radio { 54 | > div:nth-child(1) .selected { 55 | color: var(--text-secondary); 56 | 57 | span { 58 | color: var(--text-secondary); 59 | } 60 | 61 | svg { 62 | fill: var(--text-secondary); 63 | } 64 | } 65 | } 66 | 67 | &.icon { 68 | button { 69 | position: relative; 70 | 71 | svg { 72 | position: absolute; 73 | right: 2px; 74 | top: 4px; 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/components/Bar.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useContext } from 'react'; 2 | import { LayoutContext } from 'renderer/context/layout'; 3 | import { useNavigate, useLocation } from 'react-router'; 4 | import { Wrapper } from '@gavetaio/ui'; 5 | import styles from './Bar.module.scss'; 6 | 7 | const Bar = () => { 8 | const [search, setSearch] = useState(''); 9 | const navigate = useNavigate(); 10 | const location = useLocation(); 11 | const { setLayout }: any = useContext(LayoutContext); 12 | 13 | const handleKeyDown = (event: any) => { 14 | if (event.keyCode === 13) { 15 | handleSearch(); 16 | } 17 | }; 18 | 19 | const handleSearch = () => { 20 | setLayout({ search }); 21 | }; 22 | 23 | const handleBack = () => { 24 | navigate(-1); 25 | }; 26 | 27 | return ( 28 |
    29 | 30 |
    31 | 34 |
    35 |
    36 |
    {location.pathname}
    37 |
    38 | setSearch(e.target.value)} 43 | onKeyDown={handleKeyDown} 44 | /> 45 |
    46 | 49 |
    50 |
    51 |
    52 | ); 53 | }; 54 | 55 | export default Bar; 56 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gavetaio/ui", 3 | "version": "0.1.0", 4 | "private": false, 5 | "main": "./lib/index.js", 6 | "module": "./src/module.ts", 7 | "types": "./global.d.ts", 8 | "author": "@gavetaio", 9 | "dependencies": { 10 | "@primer/octicons-react": "^16.2.0", 11 | "@types/node": "^16.11.19", 12 | "@types/react": "^18.0.9", 13 | "@types/react-dom": "^18.0.3", 14 | "array-unique": "^0.3.2", 15 | "clipboard-copy": "^4.0.1", 16 | "node-sass": "^7.0.1", 17 | "react": "^18.1.0", 18 | "react-dom": "^18.1.0", 19 | "react-dropzone": "^11.4.2", 20 | "react-json-pretty": "^2.2.0", 21 | "react-scripts": "5.0.0", 22 | "typescript": "^4.5.4" 23 | }, 24 | "scripts": { 25 | "build": "react-scripts build", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "devDependencies": { 47 | "eslint": "^8.6.0", 48 | "eslint-config-airbnb-base": "^15.0.0", 49 | "eslint-config-erb": "^4.0.0-alpha.0", 50 | "eslint-plugin-compat": "^4.0.1", 51 | "eslint-plugin-import": "^2.25.4", 52 | "eslint-plugin-jsx-a11y": "^6.5.1", 53 | "eslint-plugin-promise": "^6.0.0", 54 | "eslint-plugin-react": "^7.28.0", 55 | "eslint-plugin-react-hooks": "^4.3.0", 56 | "typescript-plugin-css-modules": "^3.4.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/useCustomState.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import { deepClone } from "@gavetaio/core"; 3 | 4 | export default function useCustomState(init, debounced = false) { 5 | const [state, setState] = useState(init); 6 | const callbackRef = useRef(null); 7 | const stateRef = useRef(init); 8 | const debouncedRef = useRef(null); 9 | 10 | const debouncedStateUpdate = () => { 11 | if (debouncedRef.current) { 12 | return; 13 | } 14 | 15 | debouncedRef.current = setTimeout(updateState, 115); 16 | }; 17 | 18 | const setCustomState = (newState, callback = null, destroy = null) => { 19 | callbackRef.current = callback; 20 | 21 | if (Array.isArray(init)) { 22 | if (Array.isArray(newState)) { 23 | stateRef.current = newState; 24 | } else { 25 | stateRef.current.push(newState); 26 | } 27 | } else if (destroy) { 28 | stateRef.current = { 29 | ...init, 30 | ...newState, 31 | }; 32 | } else { 33 | stateRef.current = deepClone({ 34 | ...stateRef.current, 35 | ...newState, 36 | }); 37 | } 38 | 39 | if (debounced) { 40 | debouncedStateUpdate(); 41 | } else { 42 | updateState(); 43 | } 44 | 45 | return stateRef.current; 46 | }; 47 | 48 | const updateState = () => { 49 | setState(stateRef.current); 50 | }; 51 | 52 | const resetState = (data = {}, callback) => { 53 | setCustomState({ ...init, ...data }, callback, true); 54 | }; 55 | 56 | useEffect(() => { 57 | if (callbackRef.current) { 58 | callbackRef.current(state); 59 | } 60 | 61 | callbackRef.current = undefined; 62 | }, [state]); 63 | 64 | return [state, setCustomState, resetState, stateRef]; 65 | } 66 | -------------------------------------------------------------------------------- /packages/ui/src/dre/hooks/useCustomState.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import { deepClone } from "@gavetaio/core"; 3 | 4 | export default function useCustomState(init, debounced = false) { 5 | const [state, setState] = useState(init); 6 | const callbackRef = useRef(null); 7 | const stateRef = useRef(init); 8 | const debouncedRef = useRef(null); 9 | 10 | const debouncedStateUpdate = () => { 11 | if (debouncedRef.current) { 12 | return; 13 | } 14 | 15 | debouncedRef.current = setTimeout(updateState, 115); 16 | }; 17 | 18 | const setCustomState = (newState, callback = null, destroy = null) => { 19 | callbackRef.current = callback; 20 | 21 | if (Array.isArray(init)) { 22 | if (Array.isArray(newState)) { 23 | stateRef.current = newState; 24 | } else { 25 | stateRef.current.push(newState); 26 | } 27 | } else if (destroy) { 28 | stateRef.current = { 29 | ...init, 30 | ...newState, 31 | }; 32 | } else { 33 | stateRef.current = deepClone({ 34 | ...stateRef.current, 35 | ...newState, 36 | }); 37 | } 38 | 39 | if (debounced) { 40 | debouncedStateUpdate(); 41 | } else { 42 | updateState(); 43 | } 44 | 45 | return stateRef.current; 46 | }; 47 | 48 | const updateState = () => { 49 | setState(stateRef.current); 50 | }; 51 | 52 | const resetState = (data = {}, callback) => { 53 | setCustomState({ ...init, ...data }, callback, true); 54 | }; 55 | 56 | useEffect(() => { 57 | if (callbackRef.current) { 58 | callbackRef.current(state); 59 | } 60 | 61 | callbackRef.current = undefined; 62 | }, [state]); 63 | 64 | return [state, setCustomState, resetState, stateRef]; 65 | } 66 | -------------------------------------------------------------------------------- /packages/elections/src/main/preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer } = require('electron'); 2 | const fs = require('fs'); 3 | 4 | contextBridge.exposeInMainWorld('electron', { 5 | unzipFile: ({ section, files }) => { 6 | ipcRenderer.send('unzip-call', { section, files }); 7 | }, 8 | fs, 9 | gc: () => { 10 | ipcRenderer.send('garbage', null); 11 | }, 12 | files: { 13 | get(params) { 14 | ipcRenderer.send('files-get', params); 15 | }, 16 | folder() { 17 | ipcRenderer.send('folder-get'); 18 | }, 19 | on(channel, func) { 20 | ipcRenderer.on(channel, (event, args) => { 21 | return func(args); 22 | }); 23 | }, 24 | }, 25 | api: { 26 | get(params) { 27 | ipcRenderer.send('api-call', params); 28 | }, 29 | on(channel, func) { 30 | ipcRenderer.on(channel, (event, args) => { 31 | return func(args); 32 | }); 33 | }, 34 | }, 35 | elections: { 36 | run(data) { 37 | ipcRenderer.send('elections-run', data); 38 | }, 39 | on(channel, func) { 40 | ipcRenderer.on(channel, (event, ...args) => { 41 | return func(...args); 42 | }); 43 | }, 44 | off(channel) { 45 | ipcRenderer.removeAllListeners(channel); 46 | }, 47 | }, 48 | ipcRenderer: { 49 | myPing() { 50 | ipcRenderer.send('ipc-example', 'ping'); 51 | }, 52 | on(channel, func) { 53 | const validChannels = ['ipc-example']; 54 | if (validChannels.includes(channel)) { 55 | ipcRenderer.on(channel, (event, ...args) => func(...args)); 56 | } 57 | }, 58 | once(channel, func) { 59 | const validChannels = ['ipc-example']; 60 | if (validChannels.includes(channel)) { 61 | ipcRenderer.once(channel, (event, ...args) => func(...args)); 62 | } 63 | }, 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Key.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState, useEffect, useRef } from "react"; 2 | import Font from "./Font"; 3 | import { Elevation } from "../../base"; 4 | // @ts-ignore 5 | import styles from "./Key.module.scss"; 6 | 7 | const Key = ({ children, onClick }: any) => { 8 | const [pressed, setPressed] = useState(false); 9 | const ref: any = useRef(); 10 | 11 | const press = useCallback(() => { 12 | return !pressed && setPressed(true); 13 | }, [pressed]); 14 | 15 | const release = useCallback(() => { 16 | return pressed && setPressed(false); 17 | }, [pressed]); 18 | 19 | const action = useCallback(() => { 20 | onClick(children); 21 | release(); 22 | }, [release, children]); 23 | 24 | useEffect(() => { 25 | if (ref.current) { 26 | ref.current.addEventListener("btnPress", (event) => { 27 | setPressed(true); 28 | setTimeout(() => { 29 | onClick(children); 30 | setPressed(false); 31 | if (event?.detail?.callback) { 32 | requestAnimationFrame(() => { 33 | event.detail.callback(); 34 | }); 35 | } 36 | }, 325); 37 | }); 38 | } 39 | }, []); 40 | 41 | return ( 42 | 63 | ); 64 | }; 65 | 66 | export default Key; 67 | -------------------------------------------------------------------------------- /packages/core/utils/transformers/boxes.js: -------------------------------------------------------------------------------- 1 | import find from 'lodash/find'; 2 | import { pushUnique } from '../general'; 3 | 4 | export const getBoxById = (id, boxes) => { 5 | return find(boxes, { id }); 6 | }; 7 | 8 | const getExtra = (reg, problemas) => { 9 | let cargoList = []; 10 | const item = problemas.find((info) => { 11 | return info?.type ? !!info.type.match(reg) : !!info.match(reg); 12 | }); 13 | 14 | if (!item) { 15 | return null; 16 | } 17 | 18 | if (Array.isArray(item.extra) && item.extra?.length) { 19 | item.extra.forEach(({ nome }) => { 20 | pushUnique(cargoList, nome); 21 | }); 22 | } else { 23 | cargoList = item.extra; 24 | } 25 | 26 | return cargoList; 27 | }; 28 | 29 | export const transformBoxesTable = (boxes, problema) => { 30 | const result = []; 31 | const reg = new RegExp(problema, 'mig'); 32 | 33 | const name = problema.match(/exposed/gim) ? 'expostos' : 'perdidos'; 34 | 35 | for (let i = 0; i < boxes.length; i += 1) { 36 | const { 37 | uf, 38 | id, 39 | municipio, 40 | absolutos, 41 | problemas, 42 | cargos, 43 | status, 44 | other = null, 45 | } = boxes[i]; 46 | const splitId = id.split('-'); 47 | let extra = null; 48 | 49 | if (problemas?.length) { 50 | extra = getExtra(reg, problemas); 51 | } 52 | 53 | const isFederal = !cargos?.prefeito && !cargos?.vereador; 54 | 55 | const row = { 56 | id, 57 | ano: splitId[1], 58 | uf, 59 | turno: splitId[2], 60 | municipio, 61 | extra, 62 | zona: splitId[4], 63 | secao: splitId[5], 64 | comparecimento: absolutos.tamanho, 65 | count: absolutos[`${name}`] || extra?.count || 0, 66 | isFederal, 67 | status, 68 | other, 69 | }; 70 | 71 | result.push(row); 72 | } 73 | 74 | return result; 75 | }; 76 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/components/Bar.module.scss: -------------------------------------------------------------------------------- 1 | .statusBar { 2 | height: 32px; 3 | display: flex; 4 | align-items: center; 5 | background-color: var(--background-active); 6 | min-height: calc(var(--bar-height) / 2); 7 | justify-content: flex-start; 8 | padding: 8px 32px; 9 | } 10 | 11 | .statusContent { 12 | display: flex; 13 | flex-direction: row; 14 | align-items: center; 15 | justify-content: space-between; 16 | } 17 | 18 | .container { 19 | -webkit-app-region: drag; 20 | position: fixed; 21 | background-color: var(--background-darker); 22 | width: calc(100% - var(--menu-width)); 23 | top: 0; 24 | left: var(--menu-width); 25 | z-index: 1000; 26 | } 27 | 28 | .path { 29 | background-color: var(--background); 30 | padding: 4px 12px; 31 | border-radius: 32px; 32 | min-width: 100px; 33 | 34 | h6 { 35 | color: var(--text-alt); 36 | font-size: var(--font-xxs); 37 | font-family: 'Courier New', Courier, monospace; 38 | font-weight: 800; 39 | text-transform: none; 40 | } 41 | } 42 | .input { 43 | display: flex; 44 | align-items: center; 45 | justify-content: flex-start; 46 | flex: 1; 47 | background-color: var(--background-dark); 48 | padding: 0 16px 0 0; 49 | height: 36px; 50 | line-height: 24px; 51 | border-radius: 32px; 52 | 53 | input { 54 | flex: 1; 55 | outline: none; 56 | } 57 | } 58 | .wrapper { 59 | display: flex; 60 | align-items: center; 61 | min-height: var(--bar-height); 62 | justify-content: flex-start; 63 | padding: 8px 24px; 64 | 65 | button:nth-child(1) { 66 | margin-right: 12px; 67 | } 68 | 69 | button:nth-child(3) { 70 | margin-left: 12px; 71 | } 72 | 73 | input { 74 | border: none; 75 | background-color: rgba(0, 0, 0, 0); 76 | height: 36px; 77 | line-height: 28px; 78 | padding-left: 10px; 79 | flex: 1; 80 | color: var(--text-primary); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/components/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | export const objectToOrderedArray = (obj, labels) => { 2 | const keys = Object.keys(obj); 3 | const result = []; 4 | for (let i = 0; i < keys.length; i += 1) { 5 | const key = keys[i]; 6 | result.push({ [`${labels[0]}`]: key, [`${labels[1]}`]: obj[key] }); 7 | } 8 | return result.sort((a, b) => b[labels[1]] - a[labels[1]]); 9 | }; 10 | 11 | export const deepClone = (object) => { 12 | return JSON.parse(JSON.stringify(object)); 13 | }; 14 | 15 | export const richObjectToArray = (obj, labels, prop) => { 16 | const cloned = deepClone(obj); 17 | const keys = Object.keys(cloned); 18 | const result = []; 19 | for (let i = 0; i < keys.length; i += 1) { 20 | const key = keys[i]; 21 | result.push({ [`${labels[0]}`]: key, [`${labels[1]}`]: cloned[key][prop] }); 22 | } 23 | return result; 24 | }; 25 | 26 | const VAGAS = { 27 | deputado_distrital: { 28 | DF: 24, 29 | }, 30 | deputado_federal: { 31 | AC: 8, 32 | AL: 9, 33 | AM: 8, 34 | AP: 8, 35 | BA: 39, 36 | CE: 22, 37 | DF: 8, 38 | ES: 10, 39 | GO: 17, 40 | MA: 18, 41 | MG: 53, 42 | MS: 8, 43 | MT: 8, 44 | PA: 17, 45 | PB: 12, 46 | PE: 25, 47 | PI: 10, 48 | PR: 30, 49 | RJ: 46, 50 | RN: 8, 51 | RO: 8, 52 | RR: 8, 53 | RS: 31, 54 | SC: 16, 55 | SE: 8, 56 | SP: 70, 57 | TO: 8, 58 | }, 59 | deputado_estadual: { 60 | AC: 24, 61 | AL: 27, 62 | AM: 24, 63 | AP: 24, 64 | BA: 63, 65 | CE: 46, 66 | DF: 24, 67 | ES: 30, 68 | GO: 41, 69 | MA: 42, 70 | MG: 77, 71 | MS: 24, 72 | MT: 24, 73 | PA: 41, 74 | PB: 36, 75 | PE: 49, 76 | PI: 30, 77 | PR: 54, 78 | RJ: 70, 79 | RN: 24, 80 | RO: 24, 81 | RR: 24, 82 | RS: 55, 83 | SC: 40, 84 | SE: 24, 85 | SP: 94, 86 | TO: 24, 87 | }, 88 | }; 89 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/SimulatorLogs.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/label-has-associated-control */ 2 | /* eslint-disable react-hooks/exhaustive-deps */ 3 | /* eslint-disable react/button-has-type */ 4 | import { TrashIcon } from "@primer/octicons-react"; 5 | import { forEachList } from "@gavetaio/core"; 6 | import { Table } from "../../base"; 7 | 8 | const logsToTable = ({ logs, events }) => { 9 | const result = { 10 | header: ["Índice", "Tipo", "Evento", "Valor", "Extra"], 11 | data: [], 12 | }; 13 | 14 | if (!logs?.length) { 15 | return result; 16 | } 17 | 18 | logs.forEach((log, index) => { 19 | if (events?.length) { 20 | if (events.indexOf(log.value?.event) === -1) { 21 | return; 22 | } 23 | } 24 | const main = { 25 | event: "", 26 | value: "", 27 | extra: [], 28 | }; 29 | forEachList(log.value, (key, value) => { 30 | if (key === "event" || key === "value") { 31 | main[key] = value; 32 | return; 33 | } 34 | main.extra.push({ key, value }); 35 | }); 36 | 37 | let type = null; 38 | 39 | const row = []; 40 | row.push(index); 41 | row.push(log.label); 42 | row.push(main.event || ""); 43 | row.push(main.value || ""); 44 | row.push(main.extra); 45 | result.data.push({ type, data: row }); 46 | }); 47 | 48 | result.data.reverse(); 49 | 50 | return result; 51 | }; 52 | 53 | const SimulatorLogs = (props: any) => { 54 | const { logs = [], events = [], onClearLogs = null } = props; 55 | 56 | const actions = [ 57 | { 58 | icon: , 59 | action: () => { 60 | if (typeof onClearLogs === "function") { 61 | onClearLogs(); 62 | } 63 | }, 64 | }, 65 | ]; 66 | 67 | return ( 68 |
    74 | ); 75 | }; 76 | 77 | export default SimulatorLogs; 78 | -------------------------------------------------------------------------------- /packages/elections/.erb/configs/webpack.config.renderer.dev.dll.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Builds the DLL for development electron renderer process 3 | */ 4 | 5 | import webpack from 'webpack'; 6 | import path from 'path'; 7 | import { merge } from 'webpack-merge'; 8 | import baseConfig from './webpack.config.base'; 9 | import webpackPaths from './webpack.paths'; 10 | import { dependencies } from '../../package.json'; 11 | import checkNodeEnv from '../scripts/check-node-env'; 12 | 13 | checkNodeEnv('development'); 14 | 15 | const dist = webpackPaths.dllPath; 16 | 17 | export default merge(baseConfig, { 18 | context: webpackPaths.rootPath, 19 | 20 | devtool: 'eval', 21 | 22 | mode: 'development', 23 | 24 | target: 'electron-renderer', 25 | 26 | externals: ['fsevents', 'crypto-browserify'], 27 | 28 | /** 29 | * Use `module` from `webpack.config.renderer.dev.js` 30 | */ 31 | module: require('./webpack.config.renderer.dev').default.module, 32 | 33 | entry: { 34 | renderer: Object.keys(dependencies || {}), 35 | }, 36 | 37 | output: { 38 | path: dist, 39 | filename: '[name].dev.dll.js', 40 | library: { 41 | name: 'renderer', 42 | type: 'var', 43 | }, 44 | }, 45 | 46 | plugins: [ 47 | new webpack.DllPlugin({ 48 | path: path.join(dist, '[name].json'), 49 | name: '[name]', 50 | }), 51 | 52 | /** 53 | * Create global constants which can be configured at compile time. 54 | * 55 | * Useful for allowing different behaviour between development builds and 56 | * release builds 57 | * 58 | * NODE_ENV should be production so that modules do not perform certain 59 | * development checks 60 | */ 61 | new webpack.EnvironmentPlugin({ 62 | NODE_ENV: 'development', 63 | }), 64 | 65 | new webpack.LoaderOptionsPlugin({ 66 | debug: true, 67 | options: { 68 | context: webpackPaths.srcPath, 69 | output: { 70 | path: webpackPaths.dllPath, 71 | }, 72 | }, 73 | }), 74 | ], 75 | }); 76 | -------------------------------------------------------------------------------- /packages/ui/src/base/Button.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Button.module.scss"; 3 | import ElevatedPressable from "./ElevatedPressable"; 4 | import { useCallback } from "react"; 5 | 6 | const Button = ({ 7 | className, 8 | type = "primary", 9 | icon, 10 | id = null, 11 | iconPosition = "right", 12 | element = null, 13 | label, 14 | children, 15 | ref, 16 | transform = false, 17 | size = "medium", 18 | attrs = {}, 19 | ...extra 20 | }: any) => { 21 | let content = label || children; 22 | let wrapper = null; 23 | const cls = [styles.container]; 24 | 25 | if (className) { 26 | cls.push(className); 27 | } 28 | 29 | wrapper = ( 30 | 31 | {content} 32 | 33 | ); 34 | 35 | if (type) { 36 | cls.push(styles[type]); 37 | } 38 | 39 | if (size) { 40 | cls.push(styles[size]); 41 | } 42 | 43 | if (extra.disabled) { 44 | cls.push(styles.disabled); 45 | } 46 | 47 | const handleClick = useCallback( 48 | (ev) => { 49 | if (extra.disabled || !extra.onClick) { 50 | return false; 51 | } 52 | extra.onClick(ev); 53 | }, 54 | [extra.onClick, extra.disabled] 55 | ); 56 | 57 | const handleLongClick = useCallback(() => {}, [ 58 | extra.onLongPress, 59 | extra.disabled, 60 | ]); 61 | 62 | const handlePressDown = () => { 63 | if (extra.onPress) { 64 | extra.onPress(); 65 | } 66 | }; 67 | 68 | const handlePressRelease = () => { 69 | if (extra.onRelease) { 70 | extra.onRelease(); 71 | } 72 | }; 73 | 74 | return ( 75 | 85 | {wrapper} 86 | 87 | ); 88 | }; 89 | 90 | export default Button; 91 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/helpers.ts: -------------------------------------------------------------------------------- 1 | const formatter = new Intl.RelativeTimeFormat('pt-BR', { 2 | numeric: 'auto', 3 | }); 4 | 5 | const DIVISIONS = [ 6 | { amount: 60, name: 'seconds' }, 7 | { amount: 60, name: 'minutes' }, 8 | { amount: 24, name: 'hours' }, 9 | { amount: 7, name: 'days' }, 10 | { amount: 4.34524, name: 'weeks' }, 11 | { amount: 12, name: 'months' }, 12 | { amount: Number.POSITIVE_INFINITY, name: 'years' }, 13 | ]; 14 | 15 | function formatTimeAgo(date: Date) { 16 | // @ts-ignore 17 | let duration = (date - new Date()) / 1000; 18 | 19 | for (let i = 0; i <= DIVISIONS.length; i += 1) { 20 | const division = DIVISIONS[i]; 21 | if (Math.abs(duration) < division.amount) { 22 | // @ts-ignore 23 | return formatter.format(Math.round(duration), division.name); 24 | } 25 | duration /= division.amount; 26 | } 27 | return null; 28 | } 29 | 30 | const roundTwo = (num) => Math.round((num + Number.EPSILON) * 100) / 100; 31 | 32 | export const transformFileList = ({ jsons }) => { 33 | const loaded = { 34 | files: [], 35 | years: [], 36 | sum: 0, 37 | }; 38 | 39 | const years = []; 40 | jsons.map((item: any) => { 41 | loaded.sum += item.size; 42 | const time = formatTimeAgo(new Date(item.time)); 43 | const year = item.name.replace(/(.*)([0-9]{4})(.*)/gim, '$2'); 44 | 45 | if (years.indexOf(year) === -1) { 46 | loaded.years.push({ label: year, value: year }); 47 | years.push(year); 48 | } 49 | 50 | // const id = item.name.replace(/\.json/, '').trim(); 51 | // let type = null; 52 | 53 | const isLoaded = false; 54 | const size: number = roundTwo(item.size / 1000 / 1000); 55 | loaded.files.push({ 56 | type: isLoaded ? null : 'disabled', 57 | data: [ 58 | item.name, 59 | { 60 | value: size, 61 | label: `${Math.round(size * 1024)}`, 62 | }, 63 | { 64 | label: time, 65 | value: Math.round(item.time), 66 | }, 67 | ], 68 | }); 69 | }); 70 | return loaded; 71 | }; 72 | -------------------------------------------------------------------------------- /packages/engine/src/data/partidos.js: -------------------------------------------------------------------------------- 1 | export const PARTIDOS = `Movimento Democrático Brasileiro | MDB | 15 2 | Partido dos Trabalhadores | PT | 13 3 | Partido da Social Democracia Brasileira | PSDB | 45 4 | Progressistas | PP | 11 5 | Partido Democrático Trabalhista | PDT | 12 6 | Partido Trabalhista Brasileiro | PTB | 14 7 | Democratas | DEM | 25 8 | Partido Liberal | PL | 22 9 | Partido Socialista Brasileiro | PSB | 40 10 | Republicanos | Republicanos | 10 11 | Cidadania | Cidadania | 23 12 | Partido Social Cristão | PSC | 20 13 | Partido Comunista do Brasil | PCdoB | 65 14 | Podemos | PODE | 19 15 | Partido Social Democrático | PSD | 55 16 | Partido Verde | PV | 43 17 | Patriota | Patriota | 51 18 | Solidariedade | Solidariedade | 77 19 | Partido Socialismo e Liberdade | PSOL | 50 20 | Avante | Avante | 70 21 | Partido da Mobilização Nacional | PMN | 33 22 | Partido Trabalhista Cristão | PTC | 36 23 | Democracia Cristã | DC | 27 24 | Partido Renovador Trabalhista Brasileiro | PRTB | 28 25 | Partido Republicano da Ordem Social | PROS | 90 26 | Partido Social Liberal | PSL | 17 27 | Partido da Mulher Brasileira | PMB | 35 28 | Rede Sustentabilidade | REDE | 18 29 | Partido Novo | NOVO | 30 30 | Partido Socialista dos Trabalhadores Unificado | PSTU | 16 31 | Partido Comunista Brasileiro | PCB | 21 32 | Partido da Causa Operária | PCO | 29 33 | Unidade Popular | UP | 80 34 | Partido Pátria Livre | PPL | 54 35 | Partido Republicano Progressista | PRP | 44 36 | REDE SUSTENTABILIDADE | REDE | 31`; 37 | 38 | export const getPartidosInfo = () => { 39 | const list = PARTIDOS.split('\n'); 40 | const result = []; 41 | list.forEach((line) => { 42 | const split = line.split('|'); 43 | const partido = { 44 | nome: split[0].trim(), 45 | sigla: split[1].trim(), 46 | numero: split[2].trim(), 47 | }; 48 | result.push(partido); 49 | }); 50 | 51 | return result; 52 | }; 53 | 54 | export const getPartidoData = (number) => { 55 | const partidos = getPartidosInfo(); 56 | const result = partidos.find((partido) => partido.numero === `${number}`); 57 | 58 | return result || null; 59 | }; 60 | -------------------------------------------------------------------------------- /packages/engine/src/data/partidos copy.js: -------------------------------------------------------------------------------- 1 | export const PARTIDOS = `Movimento Democrático Brasileiro | MDB | 15 2 | Partido dos Trabalhadores | PT | 13 3 | Partido da Social Democracia Brasileira | PSDB | 45 4 | Progressistas | PP | 11 5 | Partido Democrático Trabalhista | PDT | 12 6 | Partido Trabalhista Brasileiro | PTB | 14 7 | Democratas | DEM | 25 8 | Partido Liberal | PL | 22 9 | Partido Socialista Brasileiro | PSB | 40 10 | Republicanos | Republicanos | 10 11 | Cidadania | Cidadania | 23 12 | Partido Social Cristão | PSC | 20 13 | Partido Comunista do Brasil | PCdoB | 65 14 | Podemos | PODE | 19 15 | Partido Social Democrático | PSD | 55 16 | Partido Verde | PV | 43 17 | Patriota | Patriota | 51 18 | Solidariedade | Solidariedade | 77 19 | Partido Socialismo e Liberdade | PSOL | 50 20 | Avante | Avante | 70 21 | Partido da Mobilização Nacional | PMN | 33 22 | Partido Trabalhista Cristão | PTC | 36 23 | Democracia Cristã | DC | 27 24 | Partido Renovador Trabalhista Brasileiro | PRTB | 28 25 | Partido Republicano da Ordem Social | PROS | 90 26 | Partido Social Liberal | PSL | 17 27 | Partido da Mulher Brasileira | PMB | 35 28 | Rede Sustentabilidade | REDE | 18 29 | Partido Novo | NOVO | 30 30 | Partido Socialista dos Trabalhadores Unificado | PSTU | 16 31 | Partido Comunista Brasileiro | PCB | 21 32 | Partido da Causa Operária | PCO | 29 33 | Unidade Popular | UP | 80 34 | Partido Pátria Livre | PPL | 54 35 | Partido Republicano Progressista | PRP | 44 36 | REDE SUSTENTABILIDADE | REDE | 31`; 37 | 38 | export const getPartidosInfo = () => { 39 | const list = PARTIDOS.split('\n'); 40 | const result = []; 41 | list.forEach((line) => { 42 | const split = line.split('|'); 43 | const partido = { 44 | nome: split[0].trim(), 45 | sigla: split[1].trim(), 46 | numero: split[2].trim(), 47 | }; 48 | result.push(partido); 49 | }); 50 | 51 | return result; 52 | }; 53 | 54 | export const getPartidoData = (number) => { 55 | const partidos = getPartidosInfo(); 56 | const result = partidos.find((partido) => partido.numero === `${number}`); 57 | 58 | return result || null; 59 | }; 60 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/NullBoxesPage/transformers.ts: -------------------------------------------------------------------------------- 1 | import { applyFilter } from '@gavetaio/core'; 2 | import { chartObjectToData } from 'renderer/helpers/data'; 3 | 4 | export const filteredNullBoxes = ({ boxes, filters }) => { 5 | const header = [ 6 | 'Cidade', 7 | 'Ano', 8 | 'UF', 9 | 'Turno', 10 | 'Zona', 11 | 'Seção', 12 | 'Comparecimento', 13 | 'Votos Perdidos', 14 | 'Status', 15 | ]; 16 | 17 | const result: any = []; 18 | const groupBy = filters?.groups?.length ? filters.groups[0] : 'ano'; 19 | 20 | const chart: any = { 21 | federal: {}, 22 | municipal: {}, 23 | }; 24 | 25 | boxes.forEach((box) => { 26 | const { 27 | ano, 28 | uf, 29 | turno, 30 | municipio, 31 | zona, 32 | secao, 33 | comparecimento, 34 | count, 35 | status, 36 | isFederal, 37 | } = box; 38 | 39 | if (ano === '1994') { 40 | return; 41 | } 42 | 43 | const chartType = isFederal ? 'federal' : 'municipal'; 44 | const group = groupBy === 'uf' ? uf : ano; 45 | 46 | if (!applyFilter({ filters, ano, uf })) { 47 | return; 48 | } 49 | 50 | if (!chart[chartType][group]) { 51 | chart[chartType][group] = { 52 | count: 0, 53 | }; 54 | } 55 | 56 | chart[chartType][group].count += count; 57 | 58 | const row = [ 59 | municipio, 60 | ano, 61 | uf, 62 | { value: turno }, 63 | { value: zona }, 64 | { value: secao }, 65 | { value: comparecimento, label: comparecimento || '-' }, 66 | { value: count }, 67 | status, 68 | '-', 69 | ]; 70 | 71 | result.push({ 72 | data: row, 73 | }); 74 | }); 75 | 76 | return { 77 | table: { 78 | header, 79 | data: result, 80 | sortDefault: 1, 81 | footer: ['count', '', '', '', '', '', 'average', 'sum', ''], 82 | }, 83 | chart: { 84 | title: 'Gráfico', 85 | labelX: 'ano eleitoral', 86 | labelY: 'número de votos anulados', 87 | ...chartObjectToData(chart), 88 | }, 89 | }; 90 | }; 91 | -------------------------------------------------------------------------------- /packages/elections/.erb/scripts/check-native-dep.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import chalk from 'chalk'; 3 | import { execSync } from 'child_process'; 4 | import { dependencies } from '../../package.json'; 5 | 6 | if (dependencies) { 7 | const dependenciesKeys = Object.keys(dependencies); 8 | const nativeDeps = fs 9 | .readdirSync('node_modules') 10 | .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); 11 | if (nativeDeps.length === 0) { 12 | process.exit(0); 13 | } 14 | try { 15 | // Find the reason for why the dependency is installed. If it is installed 16 | // because of a devDependency then that is okay. Warn when it is installed 17 | // because of a dependency 18 | const { dependencies: dependenciesObject } = JSON.parse( 19 | execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString() 20 | ); 21 | const rootDependencies = Object.keys(dependenciesObject); 22 | const filteredRootDependencies = rootDependencies.filter((rootDependency) => 23 | dependenciesKeys.includes(rootDependency) 24 | ); 25 | if (filteredRootDependencies.length > 0) { 26 | const plural = filteredRootDependencies.length > 1; 27 | console.log(` 28 | ${chalk.whiteBright.bgYellow.bold( 29 | 'Webpack does not work with native dependencies.' 30 | )} 31 | ${chalk.bold(filteredRootDependencies.join(', '))} ${ 32 | plural ? 'are native dependencies' : 'is a native dependency' 33 | } and should be installed inside of the "./release/app" folder. 34 | First, uninstall the packages from "./package.json": 35 | ${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} 36 | ${chalk.bold( 37 | 'Then, instead of installing the package to the root "./package.json":' 38 | )} 39 | ${chalk.whiteBright.bgRed.bold('npm install your-package')} 40 | ${chalk.bold('Install the package to "./release/app/package.json"')} 41 | ${chalk.whiteBright.bgGreen.bold('cd ./release/app && npm install your-package')} 42 | Read more about native dependencies at: 43 | ${chalk.bold( 44 | 'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure' 45 | )} 46 | `); 47 | process.exit(1); 48 | } 49 | } catch (e) { 50 | console.log('Native dependencies could not be checked'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/components/Dropper.module.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | padding: var(--unit-v-3) var(--unit-h-3); 3 | margin-top: 12px; 4 | display: flex; 5 | align-items: center; 6 | justify-content: flex-end; 7 | 8 | > button { 9 | &:nth-child(1) { 10 | margin-left: 0; 11 | } 12 | margin-left: var(--unit-h-3); 13 | } 14 | } 15 | 16 | .container { 17 | position: relative; 18 | height: 144px; 19 | width: 100%; 20 | z-index: 10; 21 | border: 2px solid var(--background-dark); 22 | border-radius: 12px; 23 | padding: var(--unit-v-3) var(--unit-h-3); 24 | background-color: var(--background-light); 25 | transition: background-color 175ms ease-out, border-color 175ms ease-out; 26 | cursor: pointer; 27 | 28 | h5 { 29 | margin-top: var(--unit-v-2); 30 | font-size: var(--font-s); 31 | color: var(--text-lighter); 32 | transition: color 175ms ease-out; 33 | } 34 | 35 | &:hover { 36 | background-color: var(--background-dark); 37 | 38 | h5 { 39 | color: var(--text-primary); 40 | } 41 | .icon { 42 | svg { 43 | fill: var(--text-primary); 44 | } 45 | } 46 | } 47 | } 48 | 49 | .icon { 50 | svg { 51 | fill: var(--text-lighter); 52 | 53 | path { 54 | transition: fill 175ms ease-out; 55 | } 56 | } 57 | } 58 | 59 | .files { 60 | padding-top: var(--unit-v-4); 61 | } 62 | 63 | .controls { 64 | display: block; 65 | padding: 24px; 66 | text-align: center; 67 | 68 | button { 69 | margin: 12px; 70 | } 71 | } 72 | 73 | .active { 74 | background-color: var(--background-lighter); 75 | } 76 | 77 | .wrapper { 78 | display: flex; 79 | position: absolute; 80 | top: 0; 81 | left: 0; 82 | right: 0; 83 | bottom: 0; 84 | align-items: center; 85 | text-align: center; 86 | justify-content: center; 87 | flex-direction: column; 88 | } 89 | 90 | .loading { 91 | display: flex; 92 | position: fixed; 93 | top: 0; 94 | left: 0; 95 | right: 0; 96 | bottom: 0; 97 | background-color: rgba(255, 0, 0, 0.25); 98 | z-index: 9001; 99 | align-items: center; 100 | justify-content: center; 101 | 102 | .status { 103 | padding: 16px; 104 | margin-top: 24px; 105 | background-color: var(--background-light); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /packages/ui/src/base/ElevatedPressable.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useRef, useCallback, useEffect } from "react"; 2 | import Elevation from "./Elevation"; 3 | 4 | const ElevatedPressable = ({ 5 | onClick = () => null, 6 | onLongPress = () => null, 7 | children, 8 | attrs = {}, 9 | ...extra 10 | }: any) => { 11 | const [pressed, setPressed] = useState(false); 12 | const ref = useRef(null); 13 | const time = useRef({ 14 | timer: null, 15 | pressed: false, 16 | }); 17 | 18 | const longPress = useCallback(() => { 19 | time.current.pressed = true; 20 | onLongPress(); 21 | }, [onLongPress]); 22 | 23 | const press = useCallback(() => { 24 | time.current.pressed = false; 25 | time.current.timer = setTimeout(longPress, 5000); 26 | return !pressed && setPressed(true); 27 | }, [pressed, longPress]); 28 | 29 | const release = useCallback(() => { 30 | clearTimeout(time.current.timer); 31 | return pressed && setPressed(false); 32 | }, [pressed]); 33 | 34 | const action = useCallback( 35 | (event) => { 36 | if (time.current.pressed) { 37 | release(); 38 | return; 39 | } 40 | onClick(event); 41 | release(); 42 | }, 43 | [release] 44 | ); 45 | 46 | const emulateTrigger = (event) => { 47 | setPressed(true); 48 | setTimeout(() => { 49 | onClick(children); 50 | setPressed(false); 51 | if (event?.detail?.callback) { 52 | requestAnimationFrame(() => { 53 | event.detail.callback(); 54 | }); 55 | } 56 | }, 325); 57 | }; 58 | 59 | useEffect(() => { 60 | if (ref?.current) { 61 | ref.current.addEventListener("btnPress", emulateTrigger); 62 | } 63 | }, []); 64 | 65 | return ( 66 | 88 | ); 89 | }; 90 | 91 | export default ElevatedPressable; 92 | -------------------------------------------------------------------------------- /packages/elections/.erb/configs/webpack.config.main.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Webpack config for production electron main process 3 | */ 4 | 5 | import path from 'path'; 6 | import webpack from 'webpack'; 7 | import { merge } from 'webpack-merge'; 8 | import TerserPlugin from 'terser-webpack-plugin'; 9 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; 10 | import baseConfig from './webpack.config.base'; 11 | import webpackPaths from './webpack.paths'; 12 | import checkNodeEnv from '../scripts/check-node-env'; 13 | import deleteSourceMaps from '../scripts/delete-source-maps'; 14 | 15 | checkNodeEnv('production'); 16 | deleteSourceMaps(); 17 | 18 | const devtoolsConfig = 19 | process.env.DEBUG_PROD === 'true' 20 | ? { 21 | devtool: 'source-map', 22 | } 23 | : {}; 24 | 25 | export default merge(baseConfig, { 26 | ...devtoolsConfig, 27 | 28 | mode: 'production', 29 | 30 | target: 'electron-main', 31 | 32 | entry: { 33 | main: path.join(webpackPaths.srcMainPath, 'main.ts'), 34 | preload: path.join(webpackPaths.srcMainPath, 'preload.js'), 35 | }, 36 | 37 | output: { 38 | path: webpackPaths.distMainPath, 39 | filename: '[name].js', 40 | }, 41 | 42 | optimization: { 43 | minimizer: [ 44 | new TerserPlugin({ 45 | parallel: true, 46 | }), 47 | ], 48 | }, 49 | 50 | plugins: [ 51 | new BundleAnalyzerPlugin({ 52 | analyzerMode: 53 | process.env.OPEN_ANALYZER === 'true' ? 'server' : 'disabled', 54 | openAnalyzer: process.env.OPEN_ANALYZER === 'true', 55 | }), 56 | 57 | /** 58 | * Create global constants which can be configured at compile time. 59 | * 60 | * Useful for allowing different behaviour between development builds and 61 | * release builds 62 | * 63 | * NODE_ENV should be production so that modules do not perform certain 64 | * development checks 65 | */ 66 | new webpack.EnvironmentPlugin({ 67 | NODE_ENV: 'production', 68 | DEBUG_PROD: false, 69 | START_MINIMIZED: false, 70 | }), 71 | ], 72 | 73 | /** 74 | * Disables webpack processing of __dirname and __filename. 75 | * If you run the bundle in node.js it falls back to these values of node.js. 76 | * https://github.com/webpack/webpack/issues/2010 77 | */ 78 | node: { 79 | __dirname: false, 80 | __filename: false, 81 | }, 82 | }); 83 | -------------------------------------------------------------------------------- /packages/ui/src/base/Button.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | --button-default-height: 42px; 3 | --color-key: var(--background-active); 4 | --elevation-state: 0.3; 5 | --button-raise-level: 4; 6 | --button-default-height: calc(var(--unit-v-4) * 1.25); 7 | --button-horizontal-padding: calc( 8 | var(--button-default-height) - var(--button-raise-level) 9 | ); 10 | 11 | display: block; 12 | position: relative; 13 | height: var(--button-default-height); 14 | width: 144px; 15 | text-align: center; 16 | 17 | .icon { 18 | > span > span { 19 | transform: translateX(0); 20 | } 21 | } 22 | 23 | > div { 24 | --elevation: var(--button-raise-level); 25 | } 26 | 27 | &:hover { 28 | > div { 29 | --elevation-state: 0.7; 30 | } 31 | } 32 | 33 | &.primary { 34 | > div { 35 | --elevation-bg-case: var(--background-active) !important; 36 | } 37 | } 38 | 39 | &.secondary { 40 | > div { 41 | --elevation-bg-case: var(--background-light) !important; 42 | } 43 | } 44 | 45 | &.danger { 46 | > div { 47 | --elevation-bg-case: var(--background-sang-light) !important; 48 | } 49 | } 50 | 51 | &.link { 52 | > div { 53 | --elevation-bg-case: var(--background-nuit); 54 | } 55 | } 56 | 57 | &.disabled { 58 | .icon { 59 | > span { 60 | --icon-bg: var(--text-dark); 61 | } 62 | } 63 | span { 64 | color: var(--text-dark); 65 | } 66 | > div { 67 | --elevation-bg-case: var(--background-light); 68 | } 69 | } 70 | 71 | &.small { 72 | // 73 | } 74 | 75 | &.medium { 76 | width: 144px; 77 | } 78 | 79 | &.large { 80 | width: 188px; 81 | } 82 | 83 | &.huge { 84 | width: 240px; 85 | } 86 | 87 | > div { 88 | } 89 | 90 | > div > div { 91 | align-items: center; 92 | justify-content: center; 93 | } 94 | 95 | &.left { 96 | .icon { 97 | transform: translateX(-50%); 98 | } 99 | } 100 | 101 | &.right { 102 | .icon { 103 | transform: translateX(50%); 104 | } 105 | } 106 | } 107 | 108 | .content { 109 | display: flex; 110 | font-size: calc(var(--font-xxs)); 111 | line-height: calc(var(--font-xxs) * 1.5); 112 | align-items: center; 113 | justify-content: center; 114 | color: var(--text-primary); 115 | } 116 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/components/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link, useLocation } from 'react-router-dom'; 3 | import styles from './Navigation.module.scss'; 4 | 5 | const links = [ 6 | { 7 | section: 'PRÆAMBULUS', 8 | links: [ 9 | { 10 | to: '/sobre', 11 | name: 'Sobre este projeto', 12 | }, 13 | ], 14 | }, 15 | { 16 | section: 'INPUT', 17 | links: [ 18 | { 19 | to: '/', 20 | name: 'Dados carregados', 21 | }, 22 | ], 23 | }, 24 | { 25 | section: 'SIMULADOR', 26 | links: [ 27 | { 28 | to: '/simulador/legado', 29 | name: 'Legado', 30 | }, 31 | { 32 | to: '/simulador/refatorado', 33 | name: 'Refatorado', 34 | }, 35 | ], 36 | }, 37 | { 38 | section: 'ANÁLISE DE DADOS', 39 | links: [ 40 | { 41 | to: '/urnas-anuladas', 42 | name: 'Urnas Anuladas', 43 | }, 44 | { 45 | to: '/sigilo-quebrado', 46 | name: 'Sigilo Quebrado', 47 | }, 48 | { 49 | to: '/votos-excluidos', 50 | name: 'Votos Excluídos', 51 | }, 52 | ], 53 | }, 54 | ]; 55 | 56 | const Navigation = () => { 57 | const location = useLocation(); 58 | 59 | const renderLink = ({ to, name }) => { 60 | const cls = []; 61 | 62 | if (location.pathname === to) { 63 | cls.push(styles.active); 64 | } 65 | return ( 66 | 67 |
    {name}
    68 | 69 | ); 70 | }; 71 | 72 | return ( 73 | 97 | ); 98 | }; 99 | 100 | export default Navigation; 101 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/ExposedPage/ExposedPage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useLayoutContext } from 'renderer/context/layout'; 3 | import { filteredExposedByBoxExposed } from './transformers'; 4 | import { 5 | Wrapper, 6 | Table, 7 | Block, 8 | Loader, 9 | LineChart, 10 | Section, 11 | Empty, 12 | } from '@gavetaio/ui'; 13 | import { useNavigation } from 'renderer/features/navigation'; 14 | 15 | const ExposedPage = () => { 16 | const { getData, apiGet }: any = useLayoutContext(); 17 | const { boxesExposed } = getData(); 18 | const { navigate } = useNavigation(); 19 | 20 | useEffect(() => { 21 | if (!boxesExposed?.length) { 22 | apiGet({ action: 'get/boxes/exposed' }); 23 | } 24 | }, []); 25 | 26 | const renderData = () => { 27 | if (typeof boxesExposed === 'undefined') { 28 | return ; 29 | } 30 | 31 | if (!boxesExposed?.length) { 32 | return navigate('/')} />; 33 | } 34 | 35 | const transformed = filteredExposedByBoxExposed({ 36 | boxes: boxesExposed, 37 | navigate, 38 | filters: { groups: ['uf'] }, 39 | }); 40 | 41 | return ( 42 | <> 43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 | 50 | ); 51 | }; 52 | 53 | return ( 54 | 55 |
    56 | 57 |

    58 | Esta análise explora a quebra do sigilo do voto de forma 59 | direta em eleições recentes. São seções onde para um determinado 60 | cargo, todos eleitores tenham votado em um mesmo candidato; para a 61 | confirmação desta ocorrência ser completa, também checamos se a 62 | mesma seção possui zero votos inválidos (brancos ou nulos) 63 | para o mesmo cargo. Veja maiores detalhes no documento{' '} 64 | 65 | gaveta.io/G2V2 66 | {' '} 67 | —{' '} 68 | 69 | Da quebra do sigilo do voto nas eleições brasileiras 70 | 71 |

    72 |
    73 |
    74 | {renderData()} 75 |
    76 | ); 77 | }; 78 | 79 | export default ExposedPage; 80 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/investigations/city-info.js: -------------------------------------------------------------------------------- 1 | const { forEachList } = require("../helpers"); 2 | 3 | const injectCandidateCount = ({ turno, data, scope = null, callback }) => { 4 | const total = { 5 | all: 0, 6 | inelegiveis: 0, 7 | deferidos: 0, 8 | }; 9 | 10 | forEachList(data.candidatos, (cargo, candidatos) => { 11 | let inelegiveis = 0; 12 | let deferidos = 0; 13 | 14 | forEachList(candidatos, (nome, info) => { 15 | if (info.situacao !== 0) { 16 | inelegiveis += 1; 17 | } 18 | if ( 19 | info.raw && 20 | info.raw.pleito.match(/^deferido$/gim) && 21 | info.raw.urna.match(/^deferido$/gim) && 22 | info.raw.atual.match(/^deferido$/gim) 23 | ) { 24 | deferidos += 1; 25 | } 26 | }); 27 | 28 | const count = Object.keys(candidatos).length; 29 | 30 | total.all += count; 31 | total.inelegiveis += inelegiveis; 32 | total.deferidos += deferidos; 33 | 34 | callback({ 35 | turno, 36 | scope, 37 | path: `cargos.${cargo}.`, 38 | data: [ 39 | { path: "candidatos", add: count }, 40 | { path: "inelegiveis", add: inelegiveis }, 41 | { path: "deferidos", add: deferidos }, 42 | ], 43 | }); 44 | }); 45 | 46 | callback({ 47 | turno, 48 | scope, 49 | path: `absolutos.`, 50 | data: [ 51 | { path: "candidatos", add: total.all }, 52 | { path: "inelegiveis", add: total.inelegiveis }, 53 | { path: "deferidos", add: total.deferidos }, 54 | ], 55 | }); 56 | }; 57 | 58 | const injectCandidate = ({ isFederal, turno, data, callback }) => { 59 | if (isFederal) { 60 | injectCandidateCount({ turno, data, scope: null, callback }); 61 | } else { 62 | forEachList(data.cidades, (codigo, cidade) => { 63 | injectCandidateCount({ turno, data: cidade, scope: codigo, callback }); 64 | }); 65 | } 66 | }; 67 | 68 | export const populateCityInfo = ({ 69 | resultados, 70 | callback, 71 | suplementares, 72 | accumulator, 73 | }) => { 74 | const isFederal = accumulator.headers.federal; 75 | 76 | forEachList(suplementares, (codigo, data) => { 77 | const { TURNO, SCOPE } = data; 78 | callback({ 79 | turno: TURNO, 80 | scope: SCOPE, 81 | path: `suplementar`, 82 | value: true, 83 | scoped: true, 84 | }); 85 | }); 86 | 87 | forEachList(resultados, (turno, data) => { 88 | const turnoNumber = turno.split("_")[1]; 89 | injectCandidate({ isFederal, turno: turnoNumber, data, callback }); 90 | }); 91 | }; 92 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Keypad.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from "react"; 2 | // @ts-ignore 3 | import styles from "./Keypad.module.scss"; 4 | import Keyboard from "./Keyboard"; 5 | import Controls from "./Controls"; 6 | import Badge from "./Badge"; 7 | 8 | function sleep(ms) { 9 | return new Promise((resolve) => setTimeout(resolve, ms)); 10 | } 11 | 12 | const Keypad = ({ 13 | onKeyPress, 14 | onFixPress, 15 | onFixLongPress, 16 | onBlankPress, 17 | onBlankLongPress, 18 | onConfirmLongPress, 19 | onConfirmPress, 20 | handleAction, 21 | badgeTitle = null, 22 | }: any) => { 23 | const ref = useRef(null); 24 | 25 | const callEvent = (num) => { 26 | return new Promise((resolve) => { 27 | const buttons = ref.current.querySelectorAll("button"); 28 | const ev = new CustomEvent("btnPress", { detail: { callback: resolve } }); 29 | if (num === "CO") { 30 | buttons[12].dispatchEvent(ev); 31 | return; 32 | } 33 | if (num === "CR") { 34 | buttons[11].dispatchEvent(ev); 35 | return; 36 | } 37 | if (num === "BR") { 38 | buttons[10].dispatchEvent(ev); 39 | return; 40 | } 41 | if (num >= 0 && num < 10) { 42 | buttons[num - 1].dispatchEvent(ev); 43 | } 44 | }); 45 | }; 46 | 47 | useEffect(() => { 48 | // 49 | ref.current.addEventListener("bundlePress", async (event) => { 50 | if (!event?.detail?.value?.length) { 51 | return; 52 | } 53 | const { value, callback } = event.detail; 54 | if (!value?.length) { 55 | return; 56 | } 57 | 58 | for (let i = 0; i < value.length; i += 1) { 59 | await callEvent(value[i]); 60 | await sleep(375); 61 | } 62 | }); 63 | }, []); 64 | 65 | return ( 66 |
    70 | 71 | { 75 | return ( 76 | 87 | ); 88 | }} 89 | /> 90 |
    91 | ); 92 | }; 93 | 94 | export default Keypad; 95 | -------------------------------------------------------------------------------- /packages/elections/src/main/api/api.ts: -------------------------------------------------------------------------------- 1 | import { 2 | transformEleicoesList, 3 | transformBoxesTable, 4 | getTitleTags, 5 | capitalize, 6 | transformEleicoesDataList, 7 | filterExposedByYear, 8 | filterExposedByUf, 9 | } from '@gavetaio/core'; 10 | 11 | const transformActionToProblem = (action) => { 12 | if (action === 'nulled') { 13 | return 'NULL_BOX'; 14 | } 15 | if (action === 'exposed') { 16 | return 'EXPOSED_VOTES'; 17 | } 18 | 19 | if (action === 'size') { 20 | return 'SIZE_DIFFERENCE'; 21 | } 22 | 23 | if (action === 'manual') { 24 | return 'MANUAL_COUNT'; 25 | } 26 | 27 | return null; 28 | }; 29 | 30 | class LocalApi { 31 | static folder: any; 32 | 33 | static memory: any; 34 | 35 | constructor({ folder, memory }) { 36 | LocalApi.folder = folder; 37 | LocalApi.memory = memory; 38 | } 39 | 40 | get({ action, params = {}, callback }) { 41 | if (action.match(/get\/election\/.*/gim)) { 42 | const info = action.split('/'); 43 | const eleicaoIds = info[2].split(','); 44 | 45 | LocalApi.memory.getDataFromIds({ ids: eleicaoIds }, (data) => { 46 | callback({ upsert: { name: 'elections', data } }); 47 | }); 48 | 49 | return; 50 | } 51 | 52 | if (action === 'get/elections') { 53 | LocalApi.memory.getElectionList((data) => { 54 | const result = transformEleicoesList(data); 55 | const years = getTitleTags(data, 'year', /[a-z]{2}-(.*)-.*/gim); 56 | const ufs = getTitleTags(data, 'uf', /([a-z]{2})-.*/gim); 57 | 58 | callback({ electionList: { data: result, tags: { years, ufs } } }); 59 | }); 60 | return; 61 | } 62 | 63 | if (action.match(/get\/boxes/gim)) { 64 | const info = action.split('/'); 65 | const problem = transformActionToProblem(info[2]); 66 | const name = `boxes${capitalize(info[2])}`; 67 | 68 | LocalApi.memory.getBoxesByProblem(problem, (data) => { 69 | try { 70 | const transformed = transformBoxesTable(data, problem); 71 | callback({ [`${name}`]: transformed }); 72 | } catch (e) {} 73 | }); 74 | return; 75 | } 76 | 77 | if (action === 'get/votes/excluded') { 78 | LocalApi.memory.getAllElections((data) => { 79 | const result = transformEleicoesDataList(data); 80 | const byYear = filterExposedByYear(result); 81 | const byUf = filterExposedByUf(result); 82 | callback({ votesExcluded: { byYear, byUf } }); 83 | }); 84 | return; 85 | } 86 | } 87 | } 88 | 89 | export default LocalApi; 90 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/ExposedPage/transformers.ts: -------------------------------------------------------------------------------- 1 | import { createChartObject, applyFilter } from '@gavetaio/core'; 2 | 3 | export const filteredExposedByBoxExposed = ({ 4 | boxes, 5 | navigate, 6 | filters, 7 | }: any) => { 8 | const header = [ 9 | 'Ano', 10 | 'Cargos', 11 | 'UF/Turno', 12 | 'Cidade', 13 | 'Zona/Seção', 14 | 'Comparecimento', 15 | 'Votos Expostos', 16 | ]; 17 | const result: any = []; 18 | const chart: any = { 19 | federal: {}, 20 | municipal: {}, 21 | }; 22 | 23 | const groupBy = filters?.groups?.length ? 'ano' : filters.groups[0]; 24 | 25 | boxes.forEach((box) => { 26 | const { 27 | id, 28 | ano, 29 | uf, 30 | turno, 31 | municipio, 32 | extra, 33 | zona, 34 | secao, 35 | comparecimento, 36 | count, 37 | isFederal, 38 | } = box; 39 | 40 | if (!applyFilter({ filters, ano, uf, cargos: extra })) { 41 | return; 42 | } 43 | 44 | const extraList = extra.map((item) => { 45 | const split = item.split('-'); 46 | let initial = split[0].slice(0, 1); 47 | if (split[0].match(/federal/)) { 48 | initial = 'f'; 49 | } 50 | if (split[0].match(/estadual/)) { 51 | initial = 'e'; 52 | } 53 | return `${split[0]}-${split[1]}`; 54 | }); 55 | 56 | const row = [ 57 | { 58 | value: ano * 1, 59 | label: ano, 60 | onClick: (event) => { 61 | navigate(id, { event }); 62 | }, 63 | }, 64 | { list: extraList }, 65 | `${uf}/${turno}`, 66 | municipio, 67 | `${zona}/${secao}`, 68 | comparecimento, 69 | count, 70 | ]; 71 | 72 | const chartType = isFederal ? 'federal' : 'municipal'; 73 | 74 | const group = groupBy === 'uf' ? uf : ano; 75 | 76 | if (!chart[chartType][group]) { 77 | chart[chartType][group] = { 78 | count: 0, 79 | total: 0, 80 | }; 81 | } 82 | 83 | chart[chartType][group].count += count; 84 | 85 | result.push({ 86 | data: row, 87 | }); 88 | }); 89 | 90 | const chartObj = createChartObject({ 91 | x: 'Ano Eleitoral', 92 | y: 'Votos com sigilo quebrado', 93 | data: chart, 94 | }); 95 | 96 | return { 97 | table: { 98 | title: 'Dados', 99 | header, 100 | data: result, 101 | sortDefault: 7, 102 | footer: ['count', '', '', '', '', 'sum', 'sum'], 103 | }, 104 | chart: chartObj, 105 | }; 106 | }; 107 | -------------------------------------------------------------------------------- /docs/excluidos.md: -------------------------------------------------------------------------------- 1 | ### Votos excluídos por indeferimento de candidatura 2 | 3 | A tabela abaixo lista apenas os **votos excluídos por indeferimento de candidatura** de 1994 a 2020. 4 | 5 | - A fonte de informação para a situação de cada candidato foi o arquivo **consulta_cand** do repositório de dados eleitorais. 6 | - Nos anos de **1994** e **1996** a informação sobre o indeferimento da candidatura não está disponível nos dados eleitorais disponíveis. 7 | - Nos anos sem informação granular foram consinderados os candidatos com status **sub-júdice**, no demais com o status **indeferido com recurso**. 8 | - A coluna de **Votos** mostra o total de votos analisados 9 | - A coluna **Excluídos** mostra o total de votos excluídos por indeferimento judicial de candidatura; consideradas tanto a invalidação de canditatos quanto partidos 10 | 11 | Seguindo as garantias estabelecidas pela **Lei de Transparência Pública** `Nº 12.527`, qualquer cidadão pode requisitar estes dados, diretamente com os órgãos oficiais responsáveis; a requisição pode ser feita da seguinte forma: `Lista do número total de votos válidos anulados por terem sido direcionados a candidatos ou partidos que se encontravam com o status de INDEFERIDOS COM RECURSO quando da data de votação do primeiro turno das eleições de 1994 a 2020; agrupados por ano eleitoral.` 12 | 13 | A partir do momento que forem captadas as razões oficiais para as anulações destas urnas, caso seja encontrado algum erro nesta listagem, qualquer pessoa pode pedir sua correção através do envio de uma `PR` para este `repositório`. 14 | 15 | | **Ano / Turno** | **Eleitores** | **Votos** | **Excluídos** | 16 | | --------------- | ------------- | --------- | ------------- | 17 | | 2020/1 | 113675327 | 227350652 | 1804923 | 18 | | 2018/1 | 117161795 | 702721150 | 3210987 | 19 | | 2016/1 | 118757770 | 237515540 | 2335801 | 20 | | 2014/1 | 114981383 | 574607706 | 1381150 | 21 | | 2012/1 | 115807514 | 231615028 | 2901057 | 22 | | 2010/1 | 111038684 | 666232102 | 3890259 | 23 | | 2008/1 | 110085205 | 220170363 | 2801402 | 24 | | 2006/1 | 104779073 | 523895343 | 539650 | 25 | | 2004/1 | 101670039 | 203338741 | 382468 | 26 | | 2002/1 | 94767056 | 568599360 | 236773 | 27 | | 2000/1 | 87416380 | 174828046 | 31810 | 28 | | 1998/1 | 83288358 | 416392739 | 86188 | 29 | | 1996/1 | 5993367 | 11986674 | — | 30 | | 1994/1 | 40904527 | 245383410 | — | 31 | | **14** | | | **19602468** | 32 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/helpers/data.ts: -------------------------------------------------------------------------------- 1 | import find from 'lodash/find'; 2 | 3 | export const getBoxById = (id, boxes) => { 4 | return find(boxes, { id }); 5 | }; 6 | 7 | export const average = (arr: any) => { 8 | return arr.reduce((a: number, b: number) => a + b, 0) / arr.length; 9 | }; 10 | 11 | export const pushUnique = (array, item) => { 12 | if (array.indexOf(item) === -1) { 13 | array.push(item); 14 | } 15 | }; 16 | 17 | export const getBoxProblemas = (box) => { 18 | const problemas = []; 19 | if (box.absolutos.perdidos) { 20 | problemas.push(['Votos Perdidos', box.absolutos.perdidos]); 21 | } 22 | return problemas; 23 | }; 24 | 25 | export const getBoxProblemasTable = (box) => { 26 | const table = { 27 | danger: true, 28 | header: ['Problema', ''], 29 | data: null, 30 | }; 31 | const problemas = getBoxProblemas(box); 32 | 33 | if (problemas?.length) { 34 | table.data = problemas; 35 | } 36 | 37 | return table; 38 | }; 39 | 40 | export const getElectionProblemasDataRow = (election) => { 41 | const problemas = []; 42 | if (election.problemas) { 43 | election.problemas.forEach((problema) => { 44 | let label = problema.type; 45 | let impact = `${problema.count} votos`; 46 | if (problema.type === 'EXPOSED_VOTES') { 47 | label = 'SIGILO QUEBRADO'; 48 | } 49 | if (problema.type === 'NULL_BOX') { 50 | label = 'URNAS ANULADAS'; 51 | impact = `${problema.count} votos perdidos`; 52 | } 53 | const splitted = problema.id.split('-'); 54 | const city = election.cidades[splitted[3]]; 55 | problemas.push([problema.id, city.nome, label, impact]); 56 | }); 57 | } 58 | return problemas; 59 | }; 60 | 61 | export const getElectionsProblemasTable = (election) => { 62 | const table = { 63 | danger: true, 64 | header: ['Urna', 'Cidade', 'Problema', 'Impacto'], 65 | data: null, 66 | }; 67 | const problemas = getElectionProblemasDataRow(election); 68 | 69 | if (problemas?.length) { 70 | table.data = problemas; 71 | } 72 | 73 | return table; 74 | }; 75 | 76 | export const chartObjectToData = (chart) => { 77 | const keys = Object.keys(chart); 78 | const lines = []; 79 | if (keys?.length) { 80 | keys.forEach((key) => { 81 | const line = { label: key, points: [] }; 82 | const keys = Object.keys(chart[key]); 83 | keys.forEach((k) => { 84 | const { count, total } = chart[key][k]; 85 | let value = count; 86 | if (total) { 87 | value = count / total; 88 | } 89 | line.points.push([k, value]); 90 | }); 91 | if (line?.points?.length) { 92 | lines.push(line); 93 | } 94 | }); 95 | } 96 | 97 | return { data: lines }; 98 | }; 99 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/NulledVotesPage/NulledVotesPage.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useLayoutContext } from 'renderer/context/layout'; 3 | import { 4 | Wrapper, 5 | Table, 6 | Block, 7 | Loader, 8 | Section, 9 | LineChart, 10 | Empty, 11 | } from '@gavetaio/ui'; 12 | import { filteredNulledSummary } from './transformers'; 13 | import { useNavigation } from 'renderer/features/navigation'; 14 | 15 | const NullVotesPage = () => { 16 | const { apiGet, getData }: any = useLayoutContext(); 17 | const { votesExcluded } = getData(); 18 | const { navigate } = useNavigation(); 19 | 20 | useEffect(() => { 21 | if (!votesExcluded?.length) { 22 | apiGet({ action: 'get/votes/excluded' }); 23 | } 24 | }, []); 25 | 26 | const renderSummary = () => { 27 | if (typeof votesExcluded === 'undefined') { 28 | return ; 29 | } 30 | 31 | if (!votesExcluded?.length) { 32 | return navigate('/')} />; 33 | } 34 | 35 | const transformed = filteredNulledSummary(votesExcluded); 36 | 37 | return ( 38 | <> 39 |
    40 | 41 |
    42 |
    43 |
    44 | 45 | 46 | ); 47 | }; 48 | 49 | return ( 50 | 51 |
    52 | 53 |

    54 | Analisamos aqui os votos anulados por{' '} 55 | indeferimento de candidatura. São candidatos que tiveram seu 56 | nome adicionado na urna eletrônica, mesmo com o indeferimento{' '} 57 | de sua candidatura, por terem apresentado recurso e estarem 58 | aguardando decisão de instância superior. Esta ocorrência tornou-se 59 | comum a partir de 2004 com a publicação da resolução{' '} 60 | 21.608/2004, e, posteriormente, pela lei 12.034/2009. 61 | Entenda os detalhes no documento:{' '} 62 | 63 | gaveta.io/g2v5 64 | {' '} 65 | —{' '} 66 | 67 | Da desproporcionalidade do direito político brasileiro 68 | 69 |

    70 |

    71 | Para localizar esta ocorrência nos dados eleitorais, precisamos 72 | separar os candidatos que estavam com o status{' '} 73 | indeferido com recurso (consulta_cand.zip) no momento 74 | da inserção na urna eletrônica, e, posteriormente, não tiveram seus 75 | votos computados situação 2, 3 ou 4. Somados a estes, 76 | também os votos direcionado para a legenda de partidos que 77 | tiveram sua inscrição indeferida. 78 |

    79 |
    80 |
    81 | {!votesExcluded ? : renderSummary()} 82 |
    83 | ); 84 | }; 85 | 86 | export default NullVotesPage; 87 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/investigations/missing-positions.js: -------------------------------------------------------------------------------- 1 | const { forEachList } = require("../helpers"); 2 | const { LoggerSingleton } = require("../../log"); 3 | 4 | const { log } = LoggerSingleton.getInstance(); 5 | 6 | const getYearFromId = (id) => { 7 | return id.replace(/^([^-]+)(-)([^-]+)(-)([^-]+)(-)(.*)/gim, "$3") * 1; 8 | }; 9 | 10 | const investigateBox = ({ 11 | accumulator, 12 | box, 13 | resultados, 14 | callback, 15 | getParentData, 16 | }) => { 17 | const { cargos, turno, codigo, scope } = box; 18 | 19 | const turnoData = resultados[`turno_${turno}`]; 20 | 21 | const parentCargos = getParentData({ 22 | turno, 23 | scope, 24 | path: "cargos", 25 | }); 26 | 27 | const { multipliers } = turnoData; 28 | const year = getYearFromId(box.id); 29 | const diff = {}; 30 | const size = box.absolutos.tamanho; 31 | 32 | if (!turnoData.cidades) { 33 | } 34 | 35 | const citySize = turnoData.cidades[codigo]?.comparecimento || 0; 36 | 37 | const extra = []; 38 | let error = 0; 39 | 40 | if (!parentCargos) { 41 | throw "#65564"; 42 | } 43 | 44 | let missingCargos = 0; 45 | 46 | forEachList(parentCargos, (cargo) => { 47 | if (cargos[cargo]) { 48 | extra.push({ cargo, votos: cargos[cargo].total }); 49 | return; 50 | } 51 | 52 | if (!multipliers[cargo]) { 53 | throw "#87657"; 54 | } 55 | 56 | let perdidos = size * multipliers[cargo]; 57 | 58 | error += perdidos; 59 | missingCargos += 1; 60 | 61 | diff[cargo] = perdidos; 62 | 63 | extra.push({ 64 | cargo, 65 | votos: 0, 66 | perdidos, 67 | }); 68 | }); 69 | 70 | if (!error) { 71 | return; 72 | } 73 | 74 | if (year >= 2010 && missingCargos) { 75 | callback({ 76 | id: box.id, 77 | path: "transition", 78 | value: true, 79 | }); 80 | return; 81 | } 82 | 83 | callback({ 84 | id: box.id, 85 | turno, 86 | scope: box.scope, 87 | path: "absolutos.perdidos", 88 | add: error, 89 | }); 90 | 91 | callback({ 92 | id: box.id, 93 | path: `problemas`, 94 | push: { 95 | type: "MISSING_POSITION", 96 | count: error, 97 | extra, 98 | }, 99 | }); 100 | 101 | callback({ 102 | turno, 103 | scope: box.scope, 104 | path: `problemas`, 105 | push: { 106 | id: box.id, 107 | type: "MISSING_POSITION", 108 | count: error, 109 | extra, 110 | }, 111 | }); 112 | 113 | log(`MISSING ${missingCargos} - ${box.id}`, { 114 | emoji: "🧨", 115 | }); 116 | }; 117 | 118 | export const investigateMissingPositions = ({ 119 | boxes, 120 | resultados, 121 | callback, 122 | getParentData, 123 | accumulator, 124 | }) => { 125 | forEachList(boxes, (id, data) => { 126 | try { 127 | investigateBox({ 128 | box: data, 129 | resultados, 130 | callback, 131 | getParentData, 132 | accumulator, 133 | }); 134 | } catch (e) { 135 | throw "#8927"; 136 | } 137 | }); 138 | }; 139 | -------------------------------------------------------------------------------- /packages/engine/src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { getDataFromFiles, clearFolder } = require("./utils"); 3 | const { Election } = require("./eleicoes/eleicoes"); 4 | const { LoggerSingleton } = require("./log"); 5 | const { 6 | PARTIALS_FOLDER, 7 | ORIGINAL_FILES_FOLDER, 8 | TEMP_FILE_PATH, 9 | FEDERAL_YEARS, 10 | } = require("./constants"); 11 | import { getAncientData } from "./data/ancient"; 12 | 13 | const { log, setLogger } = LoggerSingleton.getInstance(); 14 | 15 | export function clearFolders(folder) { 16 | clearFolder(`${folder}/${PARTIALS_FOLDER}`); 17 | clearFolder(`${folder}/${ORIGINAL_FILES_FOLDER}`); 18 | } 19 | 20 | export function getAncientHistory(folder) { 21 | return getAncientData(); 22 | } 23 | 24 | export function setupFolders(folder) { 25 | if (!fs.existsSync(`${folder}/outputs`)) { 26 | fs.mkdirSync(`${folder}/outputs`); 27 | } 28 | 29 | if (!fs.existsSync(`${folder}/outputs/data`)) { 30 | fs.mkdirSync(`${folder}/outputs/data`); 31 | } 32 | if (!fs.existsSync(`${folder}/${ORIGINAL_FILES_FOLDER}`)) { 33 | fs.mkdirSync(`${folder}/${ORIGINAL_FILES_FOLDER}`); 34 | } 35 | if (!fs.existsSync(`${folder}/${PARTIALS_FOLDER}`)) { 36 | fs.mkdirSync(`${folder}/${PARTIALS_FOLDER}`); 37 | } 38 | if (!fs.existsSync(`${folder}/${TEMP_FILE_PATH}`)) { 39 | fs.mkdirSync(`${folder}/${TEMP_FILE_PATH}`); 40 | } 41 | } 42 | 43 | export async function runPath({ files, section, folder, logger, callback }) { 44 | setLogger(logger); 45 | 46 | global.appFolder = folder; 47 | 48 | const election = new Election(); 49 | 50 | if (!files?.length) { 51 | log(["NO FILES FOUND", "REVIEW YOUR data FOLDER AND --input ARGUMENT"], { 52 | emoji: "🛑", 53 | }); 54 | return; 55 | } 56 | 57 | log(["FILES FOUND", files.join(", ")], { 58 | emoji: "🚀", 59 | }); 60 | 61 | const splitted = section.split("-"); 62 | 63 | const uf = splitted[0]; 64 | const ano = splitted[1]; 65 | const federal = FEDERAL_YEARS.indexOf(ano) !== -1; 66 | log(files, { emoji: "🛑", marginTop: true }); 67 | const header = { uf, ano, federal }; 68 | if (files.length === 1 && files[0].match(/_BR/gim)) { 69 | header.presidential = true; 70 | } 71 | 72 | election.resetAccumulator(header); 73 | 74 | try { 75 | await getDataFromFiles( 76 | files, 77 | header, 78 | (data) => { 79 | election.populateFromLine(data); 80 | }, 81 | (data) => { 82 | election.isCountryCodeWithinScope(data); 83 | } 84 | ); 85 | } catch (e) { 86 | log("UNKOWN ERROR", { emoji: "🛑", marginTop: true }); 87 | clearFolders(folder); 88 | callback({ error: true }); 89 | } 90 | 91 | election.updateGeneralInfo(); 92 | 93 | election.printer({ 94 | type: "print_ballots", 95 | extra: [], 96 | removed: ["zonas", "coligacoes"], 97 | }); 98 | 99 | log("ALL DONE 🐍🪵", { emoji: "☑️ " }); 100 | 101 | log("SPEAK UP, SPEAK LOUD → @GAVETAIO", { 102 | emoji: "📣", 103 | }); 104 | 105 | clearFolders(folder); 106 | callback({ error: false }); 107 | } 108 | -------------------------------------------------------------------------------- /packages/ui/src/dre/candidate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/engine/src/eleicoes/printer/utils.js: -------------------------------------------------------------------------------- 1 | const find = require("lodash/find"); 2 | const findIndex = require("lodash/findIndex"); 3 | const fs = require("fs"); 4 | const { LoggerSingleton } = require("../../log"); 5 | const { deepClone } = require("../../utils"); 6 | 7 | const { log } = LoggerSingleton.getInstance(); 8 | const FILE_OUTPUTS = "outputs/data"; 9 | 10 | export const pushUniqueBox = (box, boxes) => { 11 | const element = find(boxes, { id: box.id }); 12 | const cloned = deepClone(box); 13 | if (!element) { 14 | boxes.push(cloned); 15 | return; 16 | } 17 | 18 | boxes[findIndex(boxes, { id: box.id })] = cloned; 19 | }; 20 | 21 | export const getFromFile = (file) => { 22 | const fullpath = `${global.appFolder}/${FILE_OUTPUTS}/${file}`; 23 | if (!fs.existsSync(fullpath)) { 24 | return null; 25 | } 26 | 27 | const content = fs.readFileSync(fullpath, { encoding: "utf8" }); 28 | 29 | if (content) { 30 | let result = null; 31 | try { 32 | result = JSON.parse(content); 33 | } catch (e) { 34 | throw "#991"; 35 | } 36 | return result; 37 | } 38 | return null; 39 | }; 40 | 41 | export const saveToFile = ({ data, filename }) => { 42 | const partials = [`${global.appFolder}/${FILE_OUTPUTS}`, filename.split("/")]; 43 | 44 | let acc = ""; 45 | for (let i = 0; i < partials.length - 1; i += 1) { 46 | acc += i === 0 ? partials[i] : `/${partials[i]}`; 47 | try { 48 | fs.mkdirSync(acc); 49 | } catch (e) {} 50 | } 51 | 52 | const content = JSON.stringify(data); 53 | 54 | fs.writeFileSync( 55 | `${global.appFolder}/${FILE_OUTPUTS}/${filename}`, 56 | content, 57 | "utf8" 58 | ); 59 | }; 60 | 61 | export const saveFiles = (list) => { 62 | if (!list || !list.length) { 63 | return false; 64 | } 65 | 66 | list.forEach(({ object, name }) => { 67 | log(["SAVING FILE", `${name}.json`], { 68 | system: true, 69 | }); 70 | saveToFile({ 71 | data: object, 72 | filename: `${name}.json`, 73 | }); 74 | 75 | log("SAVED", { emoji: "☑️ " }); 76 | }); 77 | }; 78 | 79 | export const refactorObject = (object, removed = []) => { 80 | const keys = Object.keys(object); 81 | const result = {}; 82 | keys.forEach((key) => { 83 | if (removed.indexOf(key) === -1) { 84 | result[key] = object[key]; 85 | } 86 | }); 87 | return result; 88 | }; 89 | 90 | export const customObjectToOrderedArray = (obj, names, limit = 100) => { 91 | const cloned = deepClone(obj); 92 | 93 | const recursively = (obj) => { 94 | const keys = Object.keys(obj); 95 | let list = []; 96 | for (let i = 0; i < keys.length; i += 1) { 97 | const key = keys[i]; 98 | const value = obj[key]; 99 | if (typeof value === "object") { 100 | list = recursively(value); 101 | } 102 | if (typeof value !== "object") { 103 | list.push({ 104 | [`${names[0]}`]: key, 105 | [`${names[1]}`]: value, 106 | }); 107 | } 108 | if (list) { 109 | obj[key] = { 110 | total: list.length, 111 | list: list.sort((a, b) => b[names[1]] - a[names[1]]).slice(0, limit), 112 | }; 113 | } 114 | } 115 | return list; 116 | }; 117 | 118 | recursively(cloned); 119 | return cloned; 120 | }; 121 | -------------------------------------------------------------------------------- /packages/ui/src/base/LineChart.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | background-color: var(--background-dark); 5 | border: 1px solid var(--background-darker); 6 | margin: 24px 0; 7 | 8 | > footer { 9 | display: flex; 10 | justify-content: flex-start; 11 | padding: 4px 24px !important; 12 | background-color: var(--background-darker); 13 | } 14 | 15 | button { 16 | margin-right: 8px; 17 | } 18 | } 19 | 20 | .graph { 21 | width: 80%; 22 | margin: 0 auto; 23 | display: block; 24 | } 25 | 26 | .title { 27 | span { 28 | color: inherit; 29 | font-size: inherit; 30 | } 31 | 32 | button { 33 | margin-left: 12px; 34 | } 35 | } 36 | 37 | .graph { 38 | line { 39 | transition: opacity 125ms ease-out; 40 | } 41 | 42 | &.graphHover { 43 | .grid { 44 | > g > line { 45 | opacity: 0.1; 46 | } 47 | } 48 | } 49 | .textHover { 50 | fill: #fff; 51 | transition: fill 125ms ease-out; 52 | } 53 | } 54 | 55 | .labels { 56 | text { 57 | font-weight: bold; 58 | } 59 | 60 | > g > :nth-last-child(1) { 61 | fill: var(--text-primary); 62 | } 63 | } 64 | 65 | .lineLabel { 66 | display: block; 67 | cursor: pointer; 68 | transition: opacity 125ms ease-out; 69 | 70 | &:hover { 71 | opacity: 1; 72 | } 73 | } 74 | 75 | .grid { 76 | > g { 77 | > line { 78 | transition: opacity 125ms ease-out; 79 | opacity: 0.35; 80 | } 81 | } 82 | } 83 | 84 | .helper { 85 | transition: opacity 125ms ease-out; 86 | opacity: 0; 87 | } 88 | 89 | .helperAnglular { 90 | opacity: 0; 91 | } 92 | 93 | .helperVisible { 94 | opacity: 1; 95 | transform: scaleX(1); 96 | } 97 | 98 | .circleGroup { 99 | text { 100 | opacity: 0; 101 | visibility: hidden; 102 | transition: opacity 125ms ease-out; 103 | } 104 | circle { 105 | cursor: pointer; 106 | transition: all 100ms ease-out; 107 | } 108 | } 109 | 110 | .line { 111 | text { 112 | transition: fill 125ms ease-out; 113 | } 114 | > :nth-child(n) { 115 | transition: opacity 125ms ease-out; 116 | } 117 | 118 | &.inactive { 119 | position: relative; 120 | z-index: -1; 121 | 122 | > :nth-child(1) { 123 | animation: hideInfo 125ms ease-out forwards; 124 | } 125 | > :nth-child(2) { 126 | animation: hideInfo 125ms ease-out forwards; 127 | } 128 | > :nth-child(3) { 129 | opacity: 0.35; 130 | } 131 | } 132 | } 133 | 134 | .lineHover { 135 | text { 136 | transition: fill 125ms ease-out; 137 | fill: #fff; 138 | } 139 | } 140 | 141 | .circleHover { 142 | text { 143 | animation: showInfo 1s forwards; 144 | } 145 | circle { 146 | transform: scale(1.6); 147 | stroke: white; 148 | fill: white; 149 | } 150 | } 151 | 152 | .animateIn { 153 | animation: showInfo 1s linear forwards; 154 | } 155 | 156 | @keyframes showInfo { 157 | 0% { 158 | visibility: hidden; 159 | opacity: 0; 160 | } 161 | 1% { 162 | visibility: visible; 163 | } 164 | 100% { 165 | opacity: 1; 166 | visibility: visible; 167 | } 168 | } 169 | 170 | @keyframes hideInfo { 171 | 0% { 172 | visibility: visible; 173 | opacity: 1; 174 | } 175 | 99% { 176 | visibility: visible; 177 | opacity: 0; 178 | } 179 | 100% { 180 | opacity: 0; 181 | visibility: hidden; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /packages/elections/src/main/data/data.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-continue */ 2 | /* eslint-disable import/prefer-default-export */ 3 | 4 | const fs = require('fs'); 5 | 6 | const getInfoFromFileName = (filename) => { 7 | const info = { 8 | id: filename.replace(/^([a-z]{2}-[0-9]{4}-[0-9]{1})(.*)/gim, '$1'), 9 | type: 'eleicao', 10 | }; 11 | 12 | if (filename.match(/-cidades/)) { 13 | info.type = 'cidades'; 14 | } 15 | 16 | if (filename.match(/-candidatos/)) { 17 | info.type = 'candidatos'; 18 | } 19 | 20 | if (filename.match(/-boxes/)) { 21 | info.type = 'boxes'; 22 | } 23 | 24 | return info; 25 | }; 26 | 27 | export const loadFilesFromReg = ({ folder, callback, reg }) => { 28 | const path = `${folder}/outputs/data`; 29 | const list = fs.readdirSync(path); 30 | const result = {}; 31 | let loaded = 0; 32 | let loading = 0; 33 | 34 | if (!list?.length) { 35 | callback(result); 36 | return; 37 | } 38 | 39 | const push = ({ data, type, id }) => { 40 | if (!result[id]) { 41 | result[id] = {}; 42 | } 43 | if (!result[id][type]) { 44 | result[id][type] = {}; 45 | } 46 | result[id][type] = data; 47 | 48 | loaded += 1; 49 | if (loaded === loading) { 50 | // 51 | callback(result); 52 | } 53 | }; 54 | 55 | // 56 | for (let i = 0; i < list.length; i += 1) { 57 | const file = list[i]; 58 | if (!file.match(reg)) { 59 | continue; 60 | } 61 | 62 | loading += 1; 63 | const info = getInfoFromFileName(file); 64 | fs.readFile(`${path}/${file}`, { encoding: 'utf8' }, (err, content) => { 65 | push({ data: JSON.parse(content), ...info }); 66 | }); 67 | } 68 | 69 | if (!loading) { 70 | callback(result); 71 | } 72 | }; 73 | 74 | export const loadFile = ({ folder, file, callback, reg }) => { 75 | const path = `${folder}/outputs/data/${file}`; 76 | const result = { 77 | data: null, 78 | ...getInfoFromFileName(file), 79 | }; 80 | fs.readFile(`${path}/${file}`, { encoding: 'utf8' }, (err, content) => { 81 | result.data = JSON.parse(content); 82 | callback(result); 83 | }); 84 | }; 85 | 86 | export const loadElectionList = ({ folder, callback }) => { 87 | const path = `${folder}/outputs/data`; 88 | const list = fs.readdirSync(path); 89 | const result = { loaded: 0 }; 90 | let loading = 0; 91 | 92 | if (!list?.length) { 93 | callback(result); 94 | return; 95 | } 96 | 97 | const push = ({ data, type, id }) => { 98 | if (!result[id]) { 99 | result[id] = {}; 100 | } 101 | if (!result[id][type]) { 102 | result[id][type] = {}; 103 | } 104 | result[id][type] = data; 105 | 106 | result.loaded += 1; 107 | if (result.loaded === loading) { 108 | // 109 | callback(result); 110 | } 111 | }; 112 | 113 | // 114 | for (let i = 0; i < list.length; i += 1) { 115 | const file = list[i]; 116 | if (!file.match(/[a-z]{2}-[0-9]{4}-[0-9]{1}.json/gim)) { 117 | continue; 118 | } 119 | 120 | loading += 1; 121 | const info = getInfoFromFileName(file); 122 | fs.readFile(`${path}/${file}`, { encoding: 'utf8' }, (err, content) => { 123 | try { 124 | const parsed = JSON.parse(content); 125 | if (parsed?.situacao?.list) { 126 | parsed.situacao.list = null; 127 | } 128 | push({ data: parsed, ...info }); 129 | } catch (e) { 130 | result.loaded += 1; 131 | } 132 | }); 133 | } 134 | 135 | if (!loading) { 136 | callback(result); 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /packages/elections/src/renderer/screens/SimuladorPage/transformers.ts: -------------------------------------------------------------------------------- 1 | import { deepClone, forEachList, forEachListBreakable } from '@gavetaio/core'; 2 | import { PARTIDOS } from '@gavetaio/engine/src/constants'; 3 | 4 | const CARGO_LABEL = { 5 | cargos: { 6 | senador: 'Senador', 7 | deputado_federal: 'Deputado Federal', 8 | deputado_estadual: 'Deputado Estadual', 9 | vereador: 'Vereador', 10 | prefeito: 'Prefeito', 11 | governador: 'Governador', 12 | presidente: 'Presidente', 13 | }, 14 | }; 15 | 16 | const WEIGHT = { 17 | senador: 3, 18 | deputado_federal: 2, 19 | deputado_estadual: 1, 20 | vereador: 1, 21 | prefeito: 2, 22 | governador: 4, 23 | presidente: 5, 24 | }; 25 | 26 | export const transformEleicao = ({ candidatos, multipliers }) => { 27 | const settings = []; 28 | const candidatosList = []; 29 | const partidosList = []; 30 | 31 | forEachList(multipliers, (cargo, value) => { 32 | const item: any = {}; 33 | 34 | item.cargo = cargo; 35 | item.label = CARGO_LABEL.cargos[cargo]; 36 | item.legenda = !!cargo.match(/vereador|deputado/gim); 37 | 38 | forEachListBreakable(candidatos[cargo], (candidato) => { 39 | const split = candidato.split('-'); 40 | item.digitos = split[1].length; 41 | return true; 42 | }); 43 | 44 | for (let i = 0; i < value; i += 1) { 45 | const cloned = deepClone(item); 46 | if (value > 1) { 47 | cloned.label = `${cloned.label} — ${i + 1}ª vaga`; 48 | } 49 | cloned.id = `${cloned.cargo}_${i}`; 50 | settings.push(cloned); 51 | } 52 | 53 | forEachListBreakable(candidatos[cargo], (candidato, data) => { 54 | const candidatoObject: any = {}; 55 | const nomeSplit = candidato.split('-'); 56 | const numero = nomeSplit[1]; 57 | const partido = numero.slice(0, 2); 58 | 59 | candidatoObject.nome = data?.nome || candidato; 60 | candidatoObject.numero = numero; 61 | candidatoObject.partido = partido; 62 | candidatoObject.cargo = cargo; 63 | candidatoObject.genero = data.genero || 2; 64 | candidatoObject.situacao = data.situacao || 0; 65 | candidatoObject.extras = data?.extras || null; 66 | candidatoObject.image = 'https://gaveta.io'; 67 | 68 | candidatosList.push(candidatoObject); 69 | return true; 70 | }); 71 | 72 | forEachListBreakable(candidatos[cargo], (candidato, data) => { 73 | const candidatoObject: any = {}; 74 | const nomeSplit = candidato.split('-'); 75 | const numero = nomeSplit[1]; 76 | const partido = numero.slice(0, 2); 77 | 78 | if (!partidosList.find((info) => info.numero === partido)) { 79 | const partidoData = PARTIDOS.find((info) => info.numero === partido); 80 | 81 | const partidoItem = { 82 | nome: partidoData?.sigla || partido, 83 | numero: partido, 84 | }; 85 | partidosList.push(partidoItem); 86 | } 87 | 88 | candidatoObject.nome = data?.nome || candidato; 89 | candidatoObject.numero = numero; 90 | candidatoObject.partido = partido; 91 | candidatoObject.cargo = cargo; 92 | candidatoObject.genero = data.genero || 2; 93 | candidatoObject.situacao = data.situacao || 0; 94 | candidatoObject.extras = data?.extras || null; 95 | candidatoObject.image = 'https://gaveta.io'; 96 | 97 | candidatosList.push(candidatoObject); 98 | return false; 99 | }); 100 | }); 101 | 102 | settings.sort((a, b) => WEIGHT[a.cargo] - WEIGHT[b.cargo]); 103 | 104 | return { settings, candidatos: candidatosList, partidos: partidosList }; 105 | }; 106 | -------------------------------------------------------------------------------- /packages/ui/src/dre/candidate.tsx: -------------------------------------------------------------------------------- 1 | export default ({ className, color }) => ( 2 | 10 | 11 | 12 | 16 | 20 | 24 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | -------------------------------------------------------------------------------- /packages/engine/src/data/alagoas.js: -------------------------------------------------------------------------------- 1 | const data1996 = `27014 7524 6867 406 251 2 | 27030 7983 7377 259 347 3 | 27057 58474 51935 2473 4066 4 | 27073 13084 11015 1007 1062 5 | 27090 4589 4193 171 225 6 | 27111 2887 2739 56 92 7 | 27138 6038 5761 106 171 8 | 27154 1071 929 77 65 9 | 27170 3066 2959 63 44 10 | 27197 10002 9316 236 450 11 | 27219 2947 2679 145 123 12 | 27235 3830 3196 416 218 13 | 27251 6800 6260 234 306 14 | 27081 2191 2093 59 39 15 | 27278 10960 9742 553 665 16 | 27294 4914 4762 107 45 17 | 27316 6920 6465 302 153 18 | 27332 7169 6341 367 461 19 | 27359 3557 3382 86 89 20 | 27375 4351 4105 100 146 21 | 27391 4479 4174 126 179 22 | 27413 7682 7065 217 400 23 | 27430 2556 2400 66 90 24 | 27456 18280 15960 837 1483 25 | 28894 6291 5549 310 432 26 | 27472 18547 16901 541 1105 27 | 27499 4641 4408 133 100 28 | 27049 4335 4012 212 111 29 | 27510 7791 7340 238 213 30 | 27537 1901 1812 23 66 31 | 27553 5979 4996 550 433 32 | 27570 11794 11084 408 302 33 | 27596 5499 5162 161 176 34 | 27618 9040 8527 281 232 35 | 27634 9016 8452 341 223 36 | 27650 6530 6099 258 173 37 | 27693 2400 2274 59 67 38 | 27731 2638 2500 72 66 39 | 27758 6963 6302 284 377 40 | 27774 1834 1740 33 61 41 | 27790 9586 8747 401 438 42 | 27812 6789 6180 408 201 43 | 27839 8065 7654 182 229 44 | 27855 252969 218517 5506 28946 45 | 27871 8822 8245 186 391 46 | 27979 2183 2120 20 43 47 | 27898 4944 4480 169 295 48 | 27910 5121 4860 128 133 49 | 27936 11488 10553 343 592 50 | 27952 6772 6391 142 239 51 | 27995 9393 8801 147 445 52 | 28010 8617 7788 303 526 53 | 28037 4987 4411 161 415 54 | 28070 3185 3035 94 56 55 | 28096 10416 9389 349 678 56 | 28118 5360 4916 203 241 57 | 28134 8597 8209 197 191 58 | 28150 3053 2923 45 85 59 | 28177 2371 2271 62 38 60 | 28193 4863 4708 64 91 61 | 28215 4783 4636 75 72 62 | 28231 2401 2311 72 18 63 | 28258 21769 19452 687 1630 64 | 28274 10022 9569 210 243 65 | 27022 3983 3753 100 130 66 | 27006 3794 3427 113 254 67 | 28290 6029 5512 244 273 68 | 28312 4662 4235 254 173 69 | 28339 17053 14994 622 1437 70 | 28355 6812 6222 279 311 71 | 28371 11130 9821 473 836 72 | 28398 1303 1210 31 62 73 | 28410 7277 6842 129 306 74 | 28436 5541 5299 103 139 75 | 28452 9687 8630 460 597 76 | 28479 3494 3295 101 98 77 | 28495 8382 7774 239 369 78 | 28517 6255 5880 181 194 79 | 28533 22743 19481 768 2494 80 | 28550 3278 2861 224 193 81 | 28576 3004 2722 84 198 82 | 28592 14869 13780 270 819 83 | 28614 5454 5057 159 238 84 | 28630 3654 3572 47 35 85 | 28657 9933 8871 555 507 86 | 28673 10039 9035 486 518 87 | 28690 10694 9623 339 732 88 | 28711 22810 19911 955 1944 89 | 28754 10902 10243 282 377 90 | 28770 4710 4254 147 309 91 | 28916 3085 2919 91 75 92 | 28932 11886 10364 553 969 93 | 28797 3139 2955 87 97 94 | 28819 6376 6038 128 210 95 | 28835 10192 9374 478 340 96 | 28851 20589 17803 966 1820 97 | 28878 11252 10350 283 619 98 | `; 99 | 100 | const getAno = (data) => { 101 | const result = {}; 102 | const dataSplit = data.split("\n").map((item) => item.trim()); 103 | dataSplit.forEach((line) => { 104 | const [codigo, comparecimento, validos, brancos, nulos] = line.split(" "); 105 | 106 | const votos = validos * 1 + brancos * 1 + nulos * 1; 107 | 108 | if (!result[codigo]) { 109 | result[codigo] = { 110 | prefeito: { 111 | brancos: 0, 112 | nulos: 0, 113 | validos: 0, 114 | nominais: 0, 115 | total: 0, 116 | }, 117 | }; 118 | } 119 | 120 | const current = result[codigo].prefeito; 121 | 122 | current.total += votos; 123 | 124 | current.validos += validos * 1; 125 | current.nominais += validos * 1; 126 | 127 | current.brancos += brancos * 1; 128 | 129 | current.nulos += nulos * 1; 130 | }); 131 | return result; 132 | }; 133 | 134 | export const getDataAlagoas = () => { 135 | return { 136 | 1996: getAno(data1996), 137 | }; 138 | }; 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Electoral Tools — @gavetaio 2 | 3 | The purpose of this repository is to share all parallel code strategies produced while crafting the word sets of the Election Drawer from the @gavetaio/liber artistic-scientific project; allowing for anyone to question and properly attempt to falsify its conclusions. 4 | 5 | - Read the full document at gaveta.io/G2V6 6 | 7 | ## Falseability — To coders 8 | 9 | Way too many words were crafted into this project, but, on its pluriversal sum, it all converges to one singularity: `the quantitative electoral integrity natural language interpretation breakdown through algorithmic code`. The main goal of this coding exercise is to question how easily we, humans, have been playing loose with natural language interpretation. 10 | 11 | The Brazilian `Superior Electoral Court`’s understanding of its own `Electoral Code` is so problematic that it opens an opportunity for this algorithmic analysis since it can be easily expressed, compared, and tested, in the face of its clear conflicts with cemented international principles and human rights guarantees. 12 | 13 | - Access the integrity challenge `README.md` with instructions on how to run the tests at packages/codex/integrity/README.md 14 | - More details on gaveta.io/G2V6/velum/election-integrity 15 | 16 | ## Falseability — To non-coders and fact-checking organizations 17 | 18 | Given the fact that we, as sentient human beings, seem unable to grasp a multidisciplinary abstraction without fancifully imagining the existence of a multitude of infinite solutions, we have deliberately pushed the falsifiable paths to be done through international comparison; but, you can still abstractly argue about them, which will also be formally reviewed. 19 | 20 | - View the 11 boolean falsifiable statements at docs/g2v6.md 21 | - Extra details at gaveta.io/G2V6 22 | 23 | ## Brazilian DRE black-box — Refactored prototype 24 | 25 | The DRE prototype was crafted as a personal challenge to understand if it would be possible to properly close the user experience on the same grounds as the current legacy UI; but, instead of ignoring, following every electoral norm from the Brazilian Electoral Code that acts in the protection of its voter intent. 26 | 27 | - For accessing the DRE emulator/prototype, run the ElectronJS app and access the `Simulador` tabs. 28 | - Extra information and online simulation on gaveta.io/G2V6/velum/brazilian-dre-blackbox 29 | 30 | 31 | 32 | ## Brazilian Electoral Database — ElectronJS application 33 | 34 | The official **Brazilian Electoral Database** available at dadosabertos.tse.jus.br lacks completeness and a unified structure. For properly interrogating its contents, it was initially developed as a cmd-line NodeJS tool, but ended up evolving into a full ElectronJS application due to the increased complexity of its investigations. The app isn't intended for non-coding users though; as it was built with a focus on being a flexible research tool. 35 | 36 | - For installing it, just run `yarn install && yarn post && yarn start`. 37 | - All the app's text is in its original **Portuguese** natural language. 38 | 39 | 40 | -------------------------------------------------------------------------------- /packages/ui/src/dre/common/Image.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import styles from "./Image.module.scss"; 3 | 4 | export default ({ className = null, color = "blue", type = null }) => { 5 | const cls = [styles.container]; 6 | 7 | if (className) { 8 | cls.push(className); 9 | } 10 | 11 | return ( 12 | 20 | 21 | 22 | 26 | 30 | 34 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ); 50 | }; 51 | --------------------------------------------------------------------------------