├── .gitignore ├── api ├── src │ ├── examples │ │ ├── README.md │ │ └── index.ts │ ├── config │ │ └── index.ts │ ├── routes │ │ ├── audit.ts │ │ ├── report.ts │ │ ├── user.ts │ │ ├── upload.ts │ │ ├── voting-tables.ts │ │ ├── routerAdapter.ts │ │ └── index.ts │ ├── controllers │ │ ├── audit.ts │ │ ├── report.ts │ │ ├── user.ts │ │ └── upload.ts │ ├── utils │ │ ├── generateUniqueId.ts │ │ ├── logger.ts │ │ ├── errorConstants.ts │ │ └── s3Utils.ts │ ├── middleware │ │ └── index.ts │ ├── constants │ │ └── index.ts │ ├── index.ts │ ├── types │ │ ├── global.d.ts │ │ └── models.ts │ ├── bootstrap.ts │ ├── generators │ │ └── swagger.ts │ └── clients │ │ └── resultadosApi │ │ ├── index.ts │ │ ├── types.ts │ │ └── enums.ts ├── Makefile ├── .gitignore ├── .prettierrc ├── docker-compose.yml ├── bin │ ├── setup_localstack.py │ └── set_env_vars ├── tsconfig.json ├── package.json ├── README.md ├── yarn-error.log ├── swagger-output.json └── swagger.yaml ├── frontend ├── .github │ └── CODEOWNERS ├── src │ ├── components │ │ ├── snackbar │ │ │ ├── styles.css │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── loadingIndicator │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── selector │ │ │ ├── index.ts │ │ │ └── Selector.tsx │ │ ├── formHeader │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── index.ts │ │ ├── button │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── progressIndicator │ │ │ ├── types.ts │ │ │ ├── styles.css │ │ │ └── index.tsx │ │ ├── dataProfile │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── input │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── imageInput │ │ │ └── index.tsx │ │ ├── flatList │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── buttonSignout │ │ │ └── index.tsx │ │ ├── uploadImage │ │ │ └── index.tsx │ │ └── navbar │ │ │ └── index.tsx │ ├── pages │ │ ├── verify-certificate │ │ │ ├── types.ts │ │ │ └── styles.css │ │ ├── login │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── profile │ │ │ ├── index.ts │ │ │ ├── styles.css │ │ │ └── profile.tsx │ │ ├── results │ │ │ ├── index.ts │ │ │ └── filter.tsx │ │ ├── dashboard │ │ │ ├── index.ts │ │ │ └── dashboard.tsx │ │ ├── not-found │ │ │ ├── index.ts │ │ │ └── notFound.tsx │ │ ├── loading-page │ │ │ ├── index.ts │ │ │ └── loadingPage.tsx │ │ ├── send-success │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── styles.css │ │ │ └── sendSuccess.tsx │ │ ├── load-information │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── styles.css │ │ ├── upload-certificate │ │ │ ├── styles.css │ │ │ └── uploadCertificate.tsx │ │ └── desk-data │ │ │ └── DeskData.tsx │ ├── hooks │ │ ├── index.ts │ │ └── utils │ │ │ ├── use-effect-once.tsx │ │ │ ├── use-query.tsx │ │ │ ├── use-is-first-render.tsx │ │ │ ├── index.ts │ │ │ ├── useToggle.tsx │ │ │ ├── use-window-size.tsx │ │ │ ├── use-copy-to-clipboard.tsx │ │ │ ├── use-navigation.tsx │ │ │ ├── use-element-size.tsx │ │ │ ├── useAxios.tsx │ │ │ └── use-event-listener.tsx │ ├── service │ │ ├── index.ts │ │ ├── api │ │ │ ├── index.ts │ │ │ └── test-service.ts │ │ └── rest.ts │ ├── vite-env.d.ts │ ├── utils │ │ ├── event-bus.ts │ │ ├── index.ts │ │ ├── utils.ts │ │ └── http.ts │ ├── assets │ │ ├── logos │ │ │ ├── fenix.png │ │ │ ├── footer.svg │ │ │ └── fenix-new.svg │ │ ├── fonts │ │ │ ├── Poppins-Italic.ttf │ │ │ ├── Poppins-Medium.ttf │ │ │ ├── Poppins-Regular.ttf │ │ │ ├── Poppins-SemiBold.ttf │ │ │ ├── Poppins-MediumItalic.ttf │ │ │ └── Poppins-SemiBoldItalic.ttf │ │ ├── images │ │ │ ├── certfFiscal-test.png │ │ │ ├── check-icon.svg │ │ │ ├── back-arrow.svg │ │ │ ├── menu.svg │ │ │ └── sad-face.svg │ │ ├── icon │ │ │ ├── selector-icon.svg │ │ │ ├── close.svg │ │ │ ├── check-icon.svg │ │ │ ├── upload.svg │ │ │ ├── check.svg │ │ │ ├── eye.svg │ │ │ ├── log-out.svg │ │ │ ├── mail-closed.svg │ │ │ ├── menu.svg │ │ │ ├── warn-icon.svg │ │ │ ├── user.svg │ │ │ ├── mail-open.svg │ │ │ ├── eye-off.svg │ │ │ ├── mail-contested.svg │ │ │ ├── mail-question.svg │ │ │ ├── mail-invalid.svg │ │ │ └── mail-person.svg │ │ └── react.svg │ ├── lib │ │ └── utils.ts │ ├── main.tsx │ ├── interfaces │ │ └── IUser.ts │ ├── App.tsx │ ├── App.css │ ├── mocks │ │ └── db.json │ ├── context │ │ └── AuthContext.tsx │ ├── routes │ │ └── routes.tsx │ └── index.css ├── .prettierrc ├── postcss.config.js ├── public │ ├── fenix.png │ └── vite.svg ├── tsconfig.node.json ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── tailwind.config.js ├── vite.config.ts ├── package.json └── tsconfig.json ├── Fuentes_de_datos ├── descargar_img_telegramas │ ├── __init__.py │ ├── 0100501926X.tiff │ └── main.py ├── alineamiento_de_imagenes │ ├── src │ │ ├── __init__.py │ │ ├── inv3d_util │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── image.cpython-310.pyc │ │ │ │ ├── load.cpython-310.pyc │ │ │ │ ├── misc.cpython-310.pyc │ │ │ │ ├── path.cpython-310.pyc │ │ │ │ ├── __init__.cpython-310.pyc │ │ │ │ └── mapping.cpython-310.pyc │ │ │ ├── mask.py │ │ │ ├── parallel.py │ │ │ ├── image.py │ │ │ ├── path.py │ │ │ ├── load.py │ │ │ └── misc.py │ │ └── inv3d_model │ │ │ ├── __init__.py │ │ │ ├── models │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-310.pyc │ │ │ │ └── model_factory.cpython-310.pyc │ │ │ ├── geotr │ │ │ │ ├── __pycache__ │ │ │ │ │ ├── geotr.cpython-310.pyc │ │ │ │ │ ├── __init__.cpython-310.pyc │ │ │ │ │ ├── extractor.cpython-310.pyc │ │ │ │ │ ├── geotr_core.cpython-310.pyc │ │ │ │ │ ├── geotr_template.cpython-310.pyc │ │ │ │ │ ├── position_encoding.cpython-310.pyc │ │ │ │ │ └── geotr_template_large.cpython-310.pyc │ │ │ │ ├── __init__.py │ │ │ │ ├── geotr.py │ │ │ │ ├── extractor.py │ │ │ │ └── position_encoding.py │ │ │ └── model_factory.py │ │ │ └── __pycache__ │ │ │ └── __init__.cpython-310.pyc │ ├── input │ │ └── elecciones │ │ │ └── README.md │ ├── models │ │ └── README.md │ ├── examples │ │ ├── 00001.jpg │ │ ├── 00002.jpg │ │ ├── 00003.jpg │ │ └── template1.png │ ├── models.yaml │ ├── requirements.txt │ ├── Dockerfile │ ├── align.py │ ├── LICENSE │ └── README.md ├── ApiResultados │ ├── .env │ ├── __init__.py │ ├── config.py │ └── api_resultados.py ├── mesas_por_localidad.csv ├── Docs │ └── Manual API descargas_v03.pdf ├── README.md └── descargador_de_telegramas │ └── descargador.php ├── .github └── codeql.yaml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /api/src/examples/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/snackbar/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/pages/verify-certificate/types.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Fuentes_de_datos/descargar_img_telegramas/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } -------------------------------------------------------------------------------- /frontend/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utils"; 2 | -------------------------------------------------------------------------------- /frontend/src/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api"; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/pages/profile/index.ts: -------------------------------------------------------------------------------- 1 | export * from './profile'; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/results/index.ts: -------------------------------------------------------------------------------- 1 | export * from './filter'; 2 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/Makefile: -------------------------------------------------------------------------------- 1 | start: 2 | ./bin/set_env_vars & 3 | docker-compose up -d -------------------------------------------------------------------------------- /frontend/src/pages/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dashboard'; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/not-found/index.ts: -------------------------------------------------------------------------------- 1 | export * from './notFound'; 2 | -------------------------------------------------------------------------------- /frontend/src/service/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./test-service"; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/loading-page/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loadingPage'; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/send-success/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sendSuccess'; 2 | -------------------------------------------------------------------------------- /Fuentes_de_datos/ApiResultados/.env: -------------------------------------------------------------------------------- 1 | URL_API = https://resultados.gob.ar/backend-difu -------------------------------------------------------------------------------- /Fuentes_de_datos/ApiResultados/__init__.py: -------------------------------------------------------------------------------- 1 | from .api_resultados import ApiResultados -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules/ 3 | dist/ 4 | package-lock.json 5 | /.vscode -------------------------------------------------------------------------------- /frontend/src/pages/load-information/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loadInformation'; 2 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import geotr 2 | -------------------------------------------------------------------------------- /frontend/src/utils/event-bus.ts: -------------------------------------------------------------------------------- 1 | import mitt from "mitt"; 2 | 3 | export const eventBus = mitt(); 4 | -------------------------------------------------------------------------------- /frontend/src/pages/send-success/types.ts: -------------------------------------------------------------------------------- 1 | export interface ISendSuccessProps { 2 | message?: string; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/pages/login/types.ts: -------------------------------------------------------------------------------- 1 | export interface ILoginProps { 2 | dni: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./event-bus"; 2 | export * from "./http"; 3 | export * from "./utils"; 4 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/input/elecciones/README.md: -------------------------------------------------------------------------------- 1 | Aquí se colocan las imagenes a ser alineadas. 2 | -------------------------------------------------------------------------------- /frontend/src/pages/load-information/types.ts: -------------------------------------------------------------------------------- 1 | export interface ILoadInformationProps { 2 | message?: string; 3 | } 4 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/models/README.md: -------------------------------------------------------------------------------- 1 | Models will be automatically downloaded and placed in this folder. -------------------------------------------------------------------------------- /frontend/src/components/loadingIndicator/types.ts: -------------------------------------------------------------------------------- 1 | export interface ILoadingIndicatorProps { 2 | className: string 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/components/selector/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Exports"; 2 | export { default as Selector } from "./Selector"; 3 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/fenix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/public/fenix.png -------------------------------------------------------------------------------- /frontend/src/components/formHeader/types.ts: -------------------------------------------------------------------------------- 1 | export interface IFormHeaderProps { 2 | title: string; 3 | routerLink: string; 4 | } 5 | -------------------------------------------------------------------------------- /api/src/config/index.ts: -------------------------------------------------------------------------------- 1 | // Global configs 2 | 3 | const config = { 4 | port: process.env.PORT || 3000, 5 | }; 6 | 7 | export default config; 8 | -------------------------------------------------------------------------------- /frontend/src/pages/profile/styles.css: -------------------------------------------------------------------------------- 1 | .profile__design { 2 | padding-top: 1.5em; 3 | } 4 | 5 | .min__height-main { 6 | min-height: 80vh; 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/assets/logos/fenix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/logos/fenix.png -------------------------------------------------------------------------------- /Fuentes_de_datos/mesas_por_localidad.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/mesas_por_localidad.csv -------------------------------------------------------------------------------- /frontend/src/assets/fonts/Poppins-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/fonts/Poppins-Italic.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/fonts/Poppins-Medium.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/fonts/Poppins-Regular.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/Poppins-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/fonts/Poppins-SemiBold.ttf -------------------------------------------------------------------------------- /frontend/src/assets/images/certfFiscal-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/images/certfFiscal-test.png -------------------------------------------------------------------------------- /frontend/src/pages/upload-certificate/styles.css: -------------------------------------------------------------------------------- 1 | .message { 2 | font-size: 144% !important; 3 | font-weight: 600; 4 | } 5 | 6 | .w-image-25 { 7 | margin-right: 25%; 8 | } 9 | -------------------------------------------------------------------------------- /Fuentes_de_datos/Docs/Manual API descargas_v03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/Docs/Manual API descargas_v03.pdf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/Poppins-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/fonts/Poppins-MediumItalic.ttf -------------------------------------------------------------------------------- /api/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "printWidth": 120, 5 | "semi": false, 6 | "arrowParens": "avoid", 7 | "quoteProps": "as-needed" 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/Poppins-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/frontend/src/assets/fonts/Poppins-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /Fuentes_de_datos/descargar_img_telegramas/0100501926X.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/descargar_img_telegramas/0100501926X.tiff -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/examples/00001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/examples/00001.jpg -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/examples/00002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/examples/00002.jpg -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/examples/00003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/examples/00003.jpg -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/examples/template1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/examples/template1.png -------------------------------------------------------------------------------- /frontend/src/assets/logos/footer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /frontend/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | export * from './dataProfile'; 4 | export * from './progressIndicator'; 5 | export * from './navbar'; 6 | export * from './snackbar'; 7 | -------------------------------------------------------------------------------- /frontend/src/components/snackbar/types.ts: -------------------------------------------------------------------------------- 1 | export interface ToastiBarProps { 2 | action?: () => void; 3 | actionText?: string; 4 | close?: boolean; 5 | twoLine?: boolean; 6 | textTwo?: string; 7 | text: string; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/components/button/types.ts: -------------------------------------------------------------------------------- 1 | export interface IButtonProps { 2 | type: 'button' | 'submit' | 'reset'; 3 | className: string; 4 | label: string; 5 | onClick?: (event: React.MouseEvent) => void; 6 | } 7 | -------------------------------------------------------------------------------- /Fuentes_de_datos/ApiResultados/config.py: -------------------------------------------------------------------------------- 1 | from dotenv import load_dotenv 2 | import os 3 | load_dotenv() 4 | 5 | 6 | class Config: 7 | def __init__(self) -> None: 8 | self.URL = os.getenv('URL_API') 9 | 10 | settings = Config() -------------------------------------------------------------------------------- /api/src/routes/audit.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { auditFiscalVotingTable } from '../controllers/audit' 3 | 4 | 5 | const router = Router() 6 | 7 | router.post('', auditFiscalVotingTable) 8 | 9 | export default router 10 | -------------------------------------------------------------------------------- /api/src/controllers/audit.ts: -------------------------------------------------------------------------------- 1 | import { RequestHandler } from 'express'; 2 | 3 | export const auditFiscalVotingTable: RequestHandler = (req, res) => { 4 | // Mocked Logic 5 | res.status(201).json({ message: 'Fiscalization recorded', data: req.body }); 6 | }; -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/image.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/image.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/load.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/load.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/misc.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/misc.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/path.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/path.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/src/assets/icon/selector-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-effect-once.tsx: -------------------------------------------------------------------------------- 1 | import { EffectCallback, useEffect } from "react"; 2 | 3 | export const useEffectOnce = (effect: EffectCallback) => { 4 | // eslint-disable-next-line react-hooks/exhaustive-deps 5 | useEffect(effect, []); 6 | }; 7 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/mapping.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/__pycache__/mapping.cpython-310.pyc -------------------------------------------------------------------------------- /api/src/utils/generateUniqueId.ts: -------------------------------------------------------------------------------- 1 | export function generateUniqueId(): string { 2 | const timestamp: number = new Date().getTime(); 3 | const randomStr: string = Math.random().toString(36).substring(2, 15); 4 | return `${timestamp}-${randomStr}`; 5 | } 6 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/src/assets/icon/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/service/rest.ts: -------------------------------------------------------------------------------- 1 | export class RestClient { 2 | public baseUrl = ""; 3 | 4 | public getUrl(url: string | number = "") { 5 | if (url) { 6 | return `/${this.baseUrl}/${url}`; 7 | } 8 | 9 | return `/${this.baseUrl}`; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/assets/images/check-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /frontend/src/components/progressIndicator/types.ts: -------------------------------------------------------------------------------- 1 | export enum ProgressStepStatus { 2 | Pending = 'pending', 3 | Active = 'active', 4 | Successful = 'successful', 5 | } 6 | 7 | export interface IProgressIndicatorProps { 8 | steps: ProgressStepStatus[]; 9 | } 10 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr.cpython-310.pyc -------------------------------------------------------------------------------- /api/src/routes/report.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { getSpecificReport, listReports } from '../controllers/report' 3 | 4 | 5 | const router = Router() 6 | 7 | router.get('', listReports) 8 | router.get('/:id', getSpecificReport) 9 | 10 | export default router 11 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/check-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /frontend/src/pages/send-success/styles.css: -------------------------------------------------------------------------------- 1 | .message { 2 | font-size: 144% !important; 3 | font-weight: 600; 4 | } 5 | 6 | .w-image-25 { 7 | margin-right: 25%; 8 | } 9 | .successfull { 10 | font-size: 144%; 11 | font-weight: 600; 12 | color: rgba(68, 68, 68, 1); 13 | } 14 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/__pycache__/model_factory.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/__pycache__/model_factory.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/extractor.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/extractor.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/src/assets/images/back-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/pages/load-information/styles.css: -------------------------------------------------------------------------------- 1 | .message { 2 | font-size: 144% !important; 3 | font-weight: 600; 4 | } 5 | 6 | .w-image-25 { 7 | margin-right: 25%; 8 | } 9 | .successfull { 10 | font-size: 144%; 11 | font-weight: 600; 12 | color: rgba(68, 68, 68, 1); 13 | } 14 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr_core.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr_core.cpython-310.pyc -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr_template.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr_template.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-query.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | 4 | export const useQuery = (param: string) => { 5 | const { search } = useLocation(); 6 | 7 | return useMemo(() => new URLSearchParams(search), [search]).get(param); 8 | }; 9 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/position_encoding.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/position_encoding.cpython-310.pyc -------------------------------------------------------------------------------- /frontend/src/assets/icon/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr_template_large.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Las-Fuerzas-Del-Cielo/Sistema-Anti-Fraude-Electoral/HEAD/Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__pycache__/geotr_template_large.cpython-310.pyc -------------------------------------------------------------------------------- /api/src/middleware/index.ts: -------------------------------------------------------------------------------- 1 | import { Middleware } from 'express' 2 | 3 | // example auth middleware 4 | export const auth: Middleware = (req, res, next) => { 5 | const { session } = req 6 | 7 | if (session?.userId) { 8 | return res.status(401).json({ error: 'Not authorized' }) 9 | } 10 | next() 11 | } 12 | -------------------------------------------------------------------------------- /Fuentes_de_datos/README.md: -------------------------------------------------------------------------------- 1 | Resultados elecciones paso y generales 2023 en formato csv : https://www.argentina.gob.ar/dine/resultados-electorales/elecciones-2023 2 | 3 | 4 | api con datos variados que pueden resultar utiles (municipios,provincias,etc): 5 | https://apis.datos.gob.ar/ 6 | documentacion : 7 | https://datos.gob.ar/apis -------------------------------------------------------------------------------- /api/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | const RESULTADOS_MININTERIOR_GOB_AR_API_BASE_URL: string = 'https://resultados.mininterior.gob.ar/api'; 2 | const RESULTADOS_MININTERIOR_GOB_AR_API_RESULTADOS_URL: string = `${RESULTADOS_MININTERIOR_GOB_AR_API_BASE_URL}/resultados`; 3 | 4 | export { 5 | RESULTADOS_MININTERIOR_GOB_AR_API_RESULTADOS_URL, 6 | } -------------------------------------------------------------------------------- /frontend/src/components/progressIndicator/styles.css: -------------------------------------------------------------------------------- 1 | .circle { 2 | width: 48px; 3 | height: 48px; 4 | } 5 | 6 | .circle .icon-check { 7 | width: 40%; 8 | height: 40%; 9 | } 10 | 11 | .stick { 12 | content: ""; 13 | flex: 1; 14 | height: 2px; 15 | } 16 | 17 | .bg-green-check { 18 | background-color: #58C299; 19 | } -------------------------------------------------------------------------------- /api/src/routes/user.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { createUser, getUserRoles, getUser, getUsers } from '../controllers/user' 3 | 4 | 5 | const router = Router() 6 | 7 | router.post('', createUser) 8 | router.get('', getUsers) 9 | router.get('/:id', getUser) 10 | router.get('/:id/roles', getUserRoles) 11 | 12 | export default router 13 | -------------------------------------------------------------------------------- /frontend/src/components/dataProfile/types.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '#/interfaces/IUser'; 2 | 3 | export interface IProfileDataProps { 4 | user: IUser; 5 | } 6 | 7 | export interface IProfileDataTableProps { 8 | title: string; 9 | text: string; 10 | } 11 | 12 | export interface IFieldProps { 13 | fieldText: IProfileDataTableProps; 14 | isLast: boolean; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/components/button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { IButtonProps } from './types'; 3 | 4 | const Button: React.FC = ({ 5 | type, 6 | className, 7 | label, 8 | onClick, 9 | }) => ( 10 | 13 | ); 14 | 15 | export default Button; 16 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-is-first-render.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export const useIsFirstRender = (): boolean => { 4 | const isFirst = useRef(true); 5 | 6 | if (isFirst.current) { 7 | isFirst.current = false; 8 | 9 | return true; 10 | } 11 | 12 | return isFirst.current; 13 | }; 14 | 15 | // usage : const isFirst = useIsFirstRender() 16 | -------------------------------------------------------------------------------- /frontend/src/pages/verify-certificate/styles.css: -------------------------------------------------------------------------------- 1 | .message { 2 | font-size: 144% !important; 3 | font-weight: 600; 4 | } 5 | 6 | .w-image-25 { 7 | margin-right: 25%; 8 | } 9 | 10 | .buttonSend { 11 | display: flex; 12 | width: 316px; 13 | height: 50px; 14 | padding: 0px 25px; 15 | justify-content: center; 16 | align-items: center; 17 | gap: 10px; 18 | 19 | } -------------------------------------------------------------------------------- /api/src/routes/upload.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import fileUpload from 'express-fileupload'; 3 | import { uploadFile } from '../controllers/upload'; 4 | 5 | const router = Router(); 6 | 7 | // Middleware para manejar archivos subidos 8 | router.use(fileUpload()); 9 | 10 | // Ruta para subir archivos 11 | router.post('', uploadFile); 12 | 13 | export default router; -------------------------------------------------------------------------------- /frontend/src/hooks/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./use-effect-once"; 2 | export * from "./use-navigation"; 3 | export * from "./use-window-size"; 4 | export * from "./useToggle"; 5 | export * from "./use-event-listener"; 6 | export * from "./use-is-first-render"; 7 | export * from "./use-copy-to-clipboard"; 8 | export * from "./use-element-size"; 9 | export * from "./use-query"; 10 | -------------------------------------------------------------------------------- /api/src/index.ts: -------------------------------------------------------------------------------- 1 | import { bootstrap } from './bootstrap'; 2 | import routes from './routes'; 3 | import config from './config'; 4 | 5 | const app = bootstrap() 6 | const { port } = config 7 | 8 | routes.forEach(({ prefix, router }) => app.use(prefix, router)); 9 | 10 | app.listen(port, () => console.log(`Server up and running. \nSwagger UI running at http://localhost:${port}/api-docs`)); -------------------------------------------------------------------------------- /api/src/routes/voting-tables.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import { getVotingTableData, searchVotingTables, reportMissingAuditor } from '../controllers/voting-tables' 3 | 4 | const router = Router() 5 | 6 | router.get('/', searchVotingTables) 7 | router.get('/:id', getVotingTableData) 8 | router.post('/:id/report-missing-auditor', reportMissingAuditor) 9 | 10 | export default router 11 | -------------------------------------------------------------------------------- /frontend/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import App from './App'; 5 | import './index.css'; 6 | 7 | ReactDOM.createRoot(document.getElementById('root')!).render( 8 | 9 | 10 | 11 | 12 | , 13 | ); 14 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/models.yaml: -------------------------------------------------------------------------------- 1 | 2 | geotr@inv3d: https://drive.google.com/u/0/uc?id=17wSb997P8pDnfobhX2M206oQwlOZ-0dK 3 | geotr@doc3d: https://drive.google.com/u/0/uc?id=1cOhFB3507ly01HcuF2jtvqM4Q0KZJtSo 4 | geotr_template@inv3d: https://drive.google.com/u/0/uc?id=1SyJPgX3iZaHTOfYc8wvxWgSLDV-NSvFS 5 | geotr_template_large@inv3d: https://drive.google.com/u/0/uc?id=10Du7hUAcClgHyY92xc3gOEcx_93hBKuV 6 | -------------------------------------------------------------------------------- /api/src/controllers/report.ts: -------------------------------------------------------------------------------- 1 | import { RequestHandler } from 'express'; 2 | 3 | export const listReports: RequestHandler = (req, res) => { 4 | // Mocked Logic 5 | res.status(200).json({ denuncias: ['Denuncia 1', 'Denuncia 2'] }); 6 | }; 7 | 8 | export const getSpecificReport: RequestHandler = (req, res) => { 9 | // Mocked Logic 10 | res.status(200).json({ denuncia: 'Specific denuncia data' }); 11 | }; 12 | -------------------------------------------------------------------------------- /frontend/src/assets/images/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/service/api/test-service.ts: -------------------------------------------------------------------------------- 1 | import { http } from "#/utils"; 2 | import { RestClient } from "../rest"; 3 | 4 | class TestClient extends RestClient { 5 | public baseUrl = "/api"; 6 | 7 | public async apiCallTest() { 8 | const response = await http.get(this.getUrl(`cualquiercosa`)); 9 | 10 | return response.data; 11 | } 12 | } 13 | 14 | export const testClient = new TestClient(); 15 | -------------------------------------------------------------------------------- /frontend/src/interfaces/IUser.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | jwt: string; 6 | email: string; 7 | password: string; 8 | dni: string; 9 | phone: string; 10 | address: string; 11 | role: string; 12 | 13 | province: string; 14 | circuit: string; 15 | table: string; 16 | 17 | createdAt: string; 18 | updatedAt: string; 19 | } 20 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | yarn.lock 15 | package-lock.json 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | 28 | .env -------------------------------------------------------------------------------- /frontend/src/hooks/utils/useToggle.tsx: -------------------------------------------------------------------------------- 1 | import { Dispatch, SetStateAction, useCallback, useState } from "react"; 2 | 3 | export const useToggle = ( 4 | defaultValue?: boolean 5 | ): [boolean, () => void, Dispatch>] => { 6 | const [value, setValue] = useState(defaultValue ?? false); 7 | 8 | const toggle = useCallback(() => setValue((prevValue) => !prevValue), []); 9 | 10 | return [value, toggle, setValue]; 11 | }; 12 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LLA - SISTEMA ANTI FRAUDE ELECTORAL 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .. import model_factory 3 | 4 | from .geotr import LitGeoTr 5 | from .geotr_template import LitGeoTrTemplate 6 | from .geotr_template_large import LitGeoTrTemplateLarge 7 | 8 | model_factory.register_model("geotr", LitGeoTr) 9 | model_factory.register_model("geotr_template", LitGeoTrTemplate) 10 | model_factory.register_model("geotr_template_large", LitGeoTrTemplateLarge) 11 | -------------------------------------------------------------------------------- /api/src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express' 2 | import { Session } from './models' 3 | import { UploadedFile } from 'express-fileupload'; 4 | 5 | declare module 'express' { 6 | export type Middleware = (req: Request, res: Response, next: NextFunction) => void; 7 | 8 | interface Request { 9 | session?: Session; 10 | files?: { 11 | [key: string]: UploadedFile | UploadedFile[]; 12 | }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/check.svg: -------------------------------------------------------------------------------- 1 | 9 | 14 | -------------------------------------------------------------------------------- /frontend/src/components/input/types.ts: -------------------------------------------------------------------------------- 1 | export interface IInputProps { 2 | label: string; 3 | type: 'text' | 'password'; 4 | id: string; 5 | placeholder: string; 6 | error?: boolean; 7 | 8 | className?: string; 9 | labelClassName?: string; 10 | inputClassName?: string; 11 | 12 | appearance?: 'outline' | 'underline' 13 | onChange: (e: React.ChangeEvent) => void; 14 | onBlur: (e: React.ChangeEvent) => void; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (ms: number | undefined) => 2 | new Promise((resolve) => { 3 | setTimeout(resolve, ms); 4 | }); 5 | 6 | export function getBase64(file: File) { 7 | return new Promise((resolve, reject) => { 8 | const reader = new FileReader(); 9 | reader.readAsDataURL(file); 10 | reader.onload = function () { 11 | resolve(reader.result as string); 12 | }; 13 | reader.onerror = function (error) { 14 | reject(error); 15 | }; 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/components/imageInput/index.tsx: -------------------------------------------------------------------------------- 1 | const ImageInput = ({ 2 | id = 'dropzone-file', 3 | handleOnChange, 4 | capture, 5 | }: { 6 | id: string; 7 | handleOnChange: (e: React.ChangeEvent) => void; 8 | capture?: undefined | 'user' | 'environment'; 9 | }) => { 10 | return ( 11 | 19 | ); 20 | }; 21 | 22 | export default ImageInput; 23 | -------------------------------------------------------------------------------- /frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from 'react'; 2 | import AppRoutes from './routes/routes'; 3 | import { LoadingPage } from './pages/loading-page'; 4 | import { AuthProvider } from './context/AuthContext'; 5 | import './App.css'; 6 | 7 | function App() { 8 | return ( 9 | 10 | {/* TODO: Agregar un spinner de carga o algun mensaje mientras se carga la app. */} 11 | }> 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /api/src/routes/routerAdapter.ts: -------------------------------------------------------------------------------- 1 | import { Router as ExpressRouter } from 'express'; 2 | 3 | class RouterAdapter { 4 | private readonly _prefix: string; 5 | private readonly _router: ExpressRouter; 6 | 7 | get prefix(): string { 8 | return this._prefix; 9 | } 10 | 11 | get router(): ExpressRouter { 12 | return this._router; 13 | } 14 | 15 | constructor(prefix: string, router: ExpressRouter) { 16 | this._prefix = `/api/${prefix}`; 17 | this._router = router; 18 | } 19 | } 20 | 21 | export default RouterAdapter; -------------------------------------------------------------------------------- /frontend/src/components/flatList/types.ts: -------------------------------------------------------------------------------- 1 | type UpdateTotalVotesFunction = (votesDifference: number) => void; 2 | 3 | export enum FlatListTypeEnum { 4 | massa = 'massa', 5 | milei = 'milei', 6 | null = 'null', 7 | appealed = 'appealed', 8 | contested = 'contested', 9 | electoralCommand = 'electoralCommand', 10 | blank = 'blank', 11 | } 12 | 13 | export interface FlatListProps { 14 | logo?: string; 15 | subTitle: string; 16 | edit?: boolean; 17 | title?: string; 18 | type: FlatListTypeEnum; 19 | votes: number; 20 | updateTotalVotes: UpdateTotalVotesFunction; 21 | } 22 | -------------------------------------------------------------------------------- /api/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import RouterAdapter from './routerAdapter' 2 | import userRoutes from './user' 3 | import votingTablesRoutes from './voting-tables' 4 | import auditRoutes from './audit' 5 | import reportRouter from './report'; 6 | import uploadRoutes from './upload' 7 | 8 | const routers: RouterAdapter[] = [ 9 | new RouterAdapter('reports', reportRouter), 10 | new RouterAdapter('user', userRoutes), 11 | new RouterAdapter('voting-tables', votingTablesRoutes), 12 | new RouterAdapter('audit', auditRoutes), 13 | new RouterAdapter('upload', uploadRoutes), 14 | ]; 15 | 16 | export default routers; -------------------------------------------------------------------------------- /Fuentes_de_datos/descargar_img_telegramas/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | from ApiResultados import ApiResultados 5 | import base64 6 | 7 | sys.path.append(os.path.join(os.path.dirname(__file__), '../')) 8 | api_client = ApiResultados() 9 | 10 | def descargar_img_telegrama(mesa_id): 11 | response = api_client.get_img_telegrama(mesa_id) 12 | binary_data = base64.b64decode(response['encodingBinary']) 13 | file_name = response["fileName"] 14 | with open(f'./{file_name}', "wb") as tiff_file: 15 | tiff_file.write(binary_data) 16 | 17 | 18 | #ejemplo 19 | descargar_img_telegrama('0100501926X') -------------------------------------------------------------------------------- /api/src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import express, { json, urlencoded } from 'express' 2 | import swaggerUi from 'swagger-ui-express' 3 | import fs from 'fs-extra' 4 | import cors from 'cors' 5 | /** 6 | * @description Bootstrap the application with middlewares & swagger endpoint 7 | */ 8 | export function bootstrap() { 9 | const app = express() 10 | app.use(cors()) 11 | app.use(json()).use(urlencoded({ extended: true })) 12 | // swagger 13 | if (!process.env.AWS_LAMBDA_FUNCTION_NAME) { 14 | app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(fs.readJsonSync('./swagger.json'))) 15 | } 16 | 17 | return app 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/log-out.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:prettier/recommended', 7 | 'plugin:@typescript-eslint/recommended-type-checked', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parser: '@typescript-eslint/parser', 12 | plugins: ['react-refresh'], 13 | rules: { 14 | 'prettier/prettier': 'warn', 15 | '@typescript-eslint/no-unused-vars': 'warn', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/requirements.txt: -------------------------------------------------------------------------------- 1 | cachetools==4.2.4 2 | diskcache==5.4.0 3 | dpath==2.0.6 4 | einops==0.6.1 5 | h5py==3.6.0 6 | icecream==2.1.2 7 | ipython==8.0.1 8 | lpips==0.1.4 9 | matplotlib==3.5.1 10 | numpy==1.22.2 11 | opencv_python==4.5.5.62 12 | opencv_transforms==0.0.6 13 | pandas==1.1.5 14 | Pillow==10.0.1 15 | plotly==5.6.0 16 | positional_encodings==6.0.1 17 | pytesseract==0.3.8 18 | python_Levenshtein==0.21.1 19 | pytorch_lightning==1.6.4 20 | pytorch_msssim==0.2.1 21 | PyYAML==6.0 22 | scikit_image==0.19.2 23 | scikit_learn==1.0.2 24 | scipy==1.8.0 25 | timm==0.5.4 26 | tqdm==4.62.3 27 | unflatten==0.1.1 28 | vit_pytorch==0.38.1 29 | gdown==4.7.1 30 | -------------------------------------------------------------------------------- /api/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | local: 4 | image: localstack/localstack 5 | container_name: localstack 6 | ports: 7 | - '4566:4566' 8 | - '4510-4559:4510-4559' 9 | - '8053:53' 10 | - '8053:53/udp' 11 | - '443:443' 12 | environment: 13 | - SERVICES=s3,sqs,dynamodb 14 | - AWS_DEFAULT_REGION=us-east-1 15 | - AWS_ACCESS_KEY_ID=test 16 | - AWS_SECRET_ACCESS_KEY=test 17 | # - DEBUG=1 18 | volumes: 19 | - './bin/setup_localstack.py:/etc/localstack/init/ready.d/setup_localstack.py' 20 | - '${TMPDIR:-/tmp/localstack}:/tmp/localstack' 21 | - '/var/run/docker.sock:/var/run/docker.sock' -------------------------------------------------------------------------------- /api/src/controllers/user.ts: -------------------------------------------------------------------------------- 1 | import { RequestHandler } from 'express' 2 | 3 | export const createUser: RequestHandler = (req, res) => { 4 | // Mocked Logic 5 | res.status(201).json({ message: 'User created', user: req.body }) 6 | } 7 | 8 | export const getUserRoles: RequestHandler = (req, res) => { 9 | // Mocked Logic 10 | const roles = ['admin', 'user'] 11 | res.status(200).json({ roles }) 12 | } 13 | 14 | export const getUser: RequestHandler = (req, res) => { 15 | // Mocked Logic 16 | res.status(200).json({ user: 'John Doe' }) 17 | } 18 | export const getUsers: RequestHandler = (req, res) => { 19 | // Mocked Logic 20 | res.status(200).json([{ userId: 1 }, { userId: 2 }]) 21 | } 22 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:11.8.0-devel-ubuntu22.04 2 | 3 | RUN apt -f install 4 | RUN apt update && apt -y dist-upgrade 5 | RUN apt install -y python3-pip git ffmpeg libsm6 libxext6 6 | 7 | RUN pip3 install torch==2.0.0+cu118 torchvision==0.15.1+cu118 torchaudio==2.0.1+cu118 --index-url https://download.pytorch.org/whl/cu118 8 | 9 | RUN apt install nvidia-cuda-toolkit nvidia-cuda-toolkit-gcc 10 | 11 | COPY requirements.txt /opt/app/requirements.txt 12 | RUN pip3 install -r /opt/app/requirements.txt 13 | RUN rm /opt/app/requirements.txt 14 | 15 | RUN pip3 install -U mypy 16 | 17 | RUN git config --global core.autocrlf true 18 | 19 | RUN apt install imagemagick 20 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/mail-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-window-size.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { useEffectOnce } from "./use-effect-once"; 3 | import { useEventListener } from "./use-event-listener"; 4 | 5 | interface WindowSize { 6 | width: number; 7 | height: number; 8 | } 9 | 10 | export function useWindowSize(): WindowSize { 11 | const [windowSize, setWindowSize] = useState({ 12 | width: 0, 13 | height: 0, 14 | }); 15 | 16 | const handleSize = () => { 17 | setWindowSize({ 18 | width: window.innerWidth, 19 | height: window.innerHeight, 20 | }); 21 | }; 22 | 23 | useEventListener("resize", handleSize); 24 | 25 | useEffectOnce(() => { 26 | handleSize(); 27 | }); 28 | 29 | return windowSize; 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | margin: 0 auto; 3 | text-align: center; 4 | } 5 | 6 | .logo { 7 | height: 6em; 8 | padding: 1.5em; 9 | will-change: filter; 10 | transition: filter 300ms; 11 | } 12 | .logo:hover { 13 | filter: drop-shadow(0 0 2em #646cffaa); 14 | } 15 | .logo.react:hover { 16 | filter: drop-shadow(0 0 2em #61dafbaa); 17 | } 18 | 19 | @keyframes logo-spin { 20 | from { 21 | transform: rotate(0deg); 22 | } 23 | to { 24 | transform: rotate(360deg); 25 | } 26 | } 27 | 28 | @media (prefers-reduced-motion: no-preference) { 29 | a:nth-of-type(2) .logo { 30 | animation: logo-spin infinite 20s linear; 31 | } 32 | } 33 | 34 | .card { 35 | padding: 2em; 36 | } 37 | 38 | .read-the-docs { 39 | color: #888; 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/pages/profile/profile.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react-lite'; 2 | import Header from '#/components/formHeader'; 3 | import { DataProfile } from '#/components'; 4 | import { ButtonSignout } from '#/components/buttonSignout'; 5 | import { useAuth } from '#/context/AuthContext'; 6 | import './styles.css'; 7 | 8 | const ProfilePage = () => { 9 | const { user } = useAuth(); 10 | return ( 11 | <> 12 |
13 |
14 | {user && } 15 | 16 |
17 | 18 | ); 19 | }; 20 | 21 | export const Profile = observer(ProfilePage); 22 | 23 | export default Profile; 24 | -------------------------------------------------------------------------------- /api/src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import { createLogger, format, transports, LoggerOptions, Logger } from 'winston'; 2 | 3 | const logger: Logger = createLogger({ 4 | level: 'info', 5 | format: format.combine( 6 | format.timestamp({ 7 | format: 'YYYY-MM-DD HH:mm:ss' 8 | }), 9 | format.errors({ stack: true }), 10 | format.splat(), 11 | format.json() 12 | ), 13 | defaultMeta: { service: 'tu-servicio-api' }, 14 | transports: [ 15 | new transports.File({ filename: 'error.log', level: 'error' }), 16 | new transports.File({ filename: 'combined.log' }) 17 | ] 18 | }); 19 | 20 | // Si no estamos en producción, imprimir también en la consola 21 | if (process.env.NODE_ENV !== 'production') { 22 | logger.add(new transports.Console({ 23 | format: format.simple() 24 | })); 25 | } 26 | 27 | export default logger; 28 | -------------------------------------------------------------------------------- /frontend/src/components/buttonSignout/index.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from 'react-router-dom'; 2 | import { useAuth } from '#/context/AuthContext'; 3 | import Button from '../button'; 4 | 5 | export const ButtonSignout = () => { 6 | const { logout } = useAuth(); 7 | const navigate = useNavigate(); 8 | 9 | const signOut = () => { 10 | logout(); 11 | navigate('/'); 12 | }; 13 | 14 | return ( 15 |
16 | {/* TODO: A ESTE BOTON HAY QUE CAMBIARLO POR EL BOTON "secundario". */} 17 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /api/src/generators/swagger.ts: -------------------------------------------------------------------------------- 1 | import swaggerAutogen from 'swagger-autogen' 2 | import fs from 'fs' 3 | import path from 'path' 4 | import config from '../config' 5 | 6 | const doc = { 7 | info: { 8 | title: 'LLA Fraud detection API', 9 | description: 'API for the LLA Fraud detection project' 10 | }, 11 | host: `localhost:${config.port}`, 12 | basePath: '/api', 13 | consumes: ['application/json'], 14 | produces: ['application/json'] 15 | } 16 | 17 | const routesDir = 'dist/src/routes' 18 | const files = fs.readdirSync(path.resolve(process.cwd(), 'dist/src/routes')) 19 | 20 | const routeFiles = files 21 | .filter(file => file.endsWith('.js') && file !== 'index.js') 22 | .map(file => path.join(routesDir, file)) 23 | 24 | const outputFile = path.resolve(process.cwd(), './swagger.json') 25 | 26 | swaggerAutogen(outputFile, routeFiles, doc) 27 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/warn-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires, no-undef 2 | const defaultTheme = require('tailwindcss/defaultTheme'); 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 7 | theme: { 8 | extend: { 9 | colors: { 10 | 'violet-brand': '#61439D', 11 | 'violet-dark': '#3C2865', 12 | 'violet-light': '#8B6ACE', 13 | 'gray-dark': '#64748B', 14 | 'gray-inactive': '#CACACB', 15 | 'gray-light': '#CBD5E1', 16 | 'text-off': '#363F45', 17 | red: '#AD3459', 18 | black: '#000000', 19 | white: '#FFFFFF', 20 | }, 21 | fontFamily: { 22 | sans: ['Poppins', ...defaultTheme.fontFamily.sans], 23 | }, 24 | }, 25 | }, 26 | plugins: [], 27 | }; 28 | -------------------------------------------------------------------------------- /api/bin/setup_localstack.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | 3 | # Initialize clients for S3, SQS, and DynamoDB connecting to LocalStack 4 | s3 = boto3.client('s3', endpoint_url='http://localhost:4566') 5 | sqs = boto3.client('sqs', endpoint_url='http://localhost:4566') 6 | dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:4566') 7 | 8 | s3.create_bucket(Bucket='lla-reports') 9 | 10 | sqs.create_queue(QueueName='process-telegrams') 11 | 12 | dynamodb.create_table( 13 | TableName='FraudPrevention', 14 | KeySchema=[ 15 | {'AttributeName': 'id', 'KeyType': 'HASH'} 16 | ], 17 | AttributeDefinitions=[ 18 | {'AttributeName': 'id', 'AttributeType': 'S'} 19 | ], 20 | ProvisionedThroughput={ 21 | 'ReadCapacityUnits': 5, 22 | 'WriteCapacityUnits': 5 23 | } 24 | ) 25 | 26 | print("Infrastructure set up successfully! 🎉") 27 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-copy-to-clipboard.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | type CopiedValue = string | null; 4 | type CopyFn = (text: string) => Promise; 5 | 6 | export const useCopyToClipboard = (): [CopyFn, CopiedValue] => { 7 | const [copiedText, setCopiedText] = useState(null); 8 | 9 | const copy: CopyFn = async (text) => { 10 | if (!navigator?.clipboard) { 11 | console.warn("Clipboard not supported"); 12 | return false; 13 | } 14 | 15 | try { 16 | await navigator.clipboard.writeText(text); 17 | setCopiedText(text); 18 | return true; 19 | } catch (error) { 20 | console.warn("Copy failed", error); 21 | setCopiedText(null); 22 | return false; 23 | } 24 | }; 25 | 26 | return [copy, copiedText]; 27 | }; 28 | 29 | // usage : const [value, copy] = useCopyToClipboard() 30 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/codeql.yaml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | # schedule: 9 | # - cron: '18 8 * * 2' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze Code 14 | runs-on: ${{ 'ubuntu-latest' }} 15 | timeout-minutes: ${{ 360 }} 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'python', 'javascript-typescript' ] 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v3 29 | 30 | - name: Initialize CodeQL 31 | uses: github/codeql-action/init@v2 32 | with: 33 | languages: ${{ matrix.language }} 34 | 35 | - name: Perform CodeQL Analysis 36 | uses: github/codeql-action/analyze@v2 37 | with: 38 | category: "/language:${{matrix.language}}" 39 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/mail-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "experimentalDecorators": true, 13 | "emitDecoratorMetadata": true, 14 | "removeComments": true, 15 | "noEmitOnError": true, 16 | "allowSyntheticDefaultImports": true, 17 | "strictPropertyInitialization": false, 18 | "noImplicitAny": true, 19 | "noImplicitThis": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noImplicitReturns": false, 23 | "noFallthroughCasesInSwitch": true, 24 | "skipLibCheck": true, 25 | "sourceMap": true 26 | }, 27 | "include": ["src/"], 28 | "exclude": ["node_modules"], 29 | "ts-node": { 30 | "transpileOnly": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/align.py: -------------------------------------------------------------------------------- 1 | # Por: Gissio 2 | # 3 | # Basado en: 4 | # * https://github.com/Gissio/safe-ocr-alignment 5 | # * https://github.com/FelixHertlein/inv3d-model 6 | 7 | import os 8 | import shutil 9 | import sys 10 | 11 | import inference 12 | 13 | if len(sys.argv) != 3: 14 | print("Usage: python3 align.py [IMAGE-FILE] [TEMPLATE-FILE]") 15 | exit(1) 16 | 17 | image_file = sys.argv[1] 18 | template_file = sys.argv[2] 19 | 20 | input_path = "input/elecciones/" 21 | 22 | shutil.copyfile(image_file, input_path + "image_1.jpg") 23 | shutil.copyfile(template_file, input_path + "template_1.jpg") 24 | 25 | print(input_path) 26 | 27 | inference.inference( 28 | model_name="geotr_template_large@inv3d", 29 | dataset="elecciones", 30 | output_shape=(1400, 860), 31 | ) 32 | 33 | shutil.copyfile( 34 | "output/elecciones - geotr_template_large@inv3d/unwarped_1.png", 35 | "output/unwarped.png", 36 | ) 37 | -------------------------------------------------------------------------------- /api/bin/set_env_vars: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : > .env # Clear out the old .env file 4 | { 5 | echo AWS_ACCESS_KEY_ID=test 6 | echo AWS_SECRET_ACCESS_KEY=test 7 | echo AWS_REGION=us-east-1 8 | echo AWS_ENDPOINT=http://localhost:4566 9 | echo DYNAMODB_ENDPOINT=http://localhost:4566/000000000000/process-telegrams 10 | echo QUEUE_URL=http://localhost:4566/000000000000/process-telegrams 11 | echo DYNAMODB_TABLE_NAME=FraudPrevention 12 | echo S3_REPORTS_BUCKET=lla-reports 13 | } >> .env 14 | 15 | 16 | wait_for_localstack() { 17 | echo "Waiting for Localstack to be ready..." 18 | 19 | # Poll the logs until we see that Localstack is ready 20 | while true; do 21 | docker logs localstack 2>&1 | grep 'Infrastructure set up successfully' && break 22 | echo "[bin/set_env_vars] Still waiting for Localstack to be ready..." 23 | sleep 1 24 | done 25 | echo "Localstack is ready." 26 | } 27 | 28 | wait_for_localstack 29 | 30 | # Start nodejs app in dev mode 31 | yarn dev -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-navigation.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | 4 | export const useNavigation = () => { 5 | const navigate = useNavigate(); 6 | 7 | const push = useCallback( 8 | (route: string) => { 9 | navigate(route); 10 | }, 11 | [navigate] 12 | ); 13 | 14 | const replace = useCallback( 15 | (route: string) => { 16 | navigate(route, { 17 | replace: true, 18 | }); 19 | }, 20 | [navigate] 21 | ); 22 | 23 | const pop = useCallback( 24 | (pagesAmount?: number) => { 25 | const pages = pagesAmount ? -pagesAmount : -1; 26 | navigate(pages); 27 | }, 28 | [navigate] 29 | ); 30 | 31 | const popToTop = useCallback(() => { 32 | navigate("/", { 33 | replace: true, 34 | }); 35 | }, [navigate]); 36 | 37 | const refresh = useCallback(() => { 38 | navigate(0); 39 | }, [navigate]); 40 | 41 | return { push, pop, replace, popToTop, refresh }; 42 | }; 43 | -------------------------------------------------------------------------------- /frontend/src/pages/not-found/notFound.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import Button from '#/components/button'; 3 | import Navbar from '#/components/navbar'; 4 | 5 | const NotFoundPage = () => { 6 | return ( 7 |
8 | 9 |
10 |
11 |

404

12 | not-found-page 17 |

Lo sentimos, página no encontrada

18 |
19 | 20 |
27 |
28 | ); 29 | }; 30 | 31 | export default NotFoundPage; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Las Fuerzas del Cielo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-element-size.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from "react"; 2 | import { useEventListener } from "./use-event-listener"; 3 | 4 | interface Size { 5 | width: number; 6 | height: number; 7 | } 8 | 9 | export function useElementSize(): [ 10 | (node: T | null) => void, 11 | Size 12 | ] { 13 | const [ref, setRef] = useState(null); 14 | const [size, setSize] = useState({ 15 | width: 0, 16 | height: 0, 17 | }); 18 | 19 | const handleSize = useCallback(() => { 20 | setSize({ 21 | width: ref?.offsetWidth || 0, 22 | height: ref?.offsetHeight || 0, 23 | }); 24 | }, [ref?.offsetHeight, ref?.offsetWidth]); 25 | 26 | useEventListener("resize", handleSize); 27 | 28 | useEffect(() => { 29 | handleSize(); 30 | // eslint-disable-next-line react-hooks/exhaustive-deps 31 | }, [ref?.offsetHeight, ref?.offsetWidth]); 32 | 33 | return [setRef, size]; 34 | } 35 | 36 | // usage : const [ref, { width, height }] = useElementSize() 37 | // Le pasamos la ref del elemento que queremos el tamaño y nos devuelve el width y height 38 | -------------------------------------------------------------------------------- /api/src/clients/resultadosApi/index.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from "axios"; 2 | 3 | import {RESULTADOS_MININTERIOR_GOB_AR_API_RESULTADOS_URL} from "../../constants"; 4 | import {GetResultadosParamsRequest, GetResultadosResponse} from "./types"; 5 | import {EleccionTipo, MesaTipo, PadronTipo, RecuentoTipo, VotosTipo, Provincia} from './enums'; 6 | 7 | export interface IResultadosApi { 8 | getResultados(GetResultadosParamsRequest): Promise; 9 | } 10 | 11 | class ResultadosApi implements IResultadosApi { 12 | private readonly baseUrl: string = RESULTADOS_MININTERIOR_GOB_AR_API_RESULTADOS_URL; 13 | 14 | async getResultados(params: GetResultadosParamsRequest): Promise { 15 | const response: AxiosResponse = 16 | await axios.get(this.baseUrl+'/getResultados', {params}); 17 | 18 | return response.data; 19 | } 20 | } 21 | 22 | export { 23 | ResultadosApi, 24 | GetResultadosParamsRequest, 25 | GetResultadosResponse, 26 | EleccionTipo, 27 | RecuentoTipo, 28 | PadronTipo, 29 | MesaTipo, 30 | VotosTipo, 31 | Provincia, 32 | } -------------------------------------------------------------------------------- /frontend/src/components/formHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { IFormHeaderProps } from './types'; 4 | 5 | const FormHeader: React.FC = ({ routerLink, title }) => { 6 | return ( 7 |
8 |
9 | 10 | Volver 15 | 16 |
17 |
18 |
19 | Logo 24 |
25 |
26 |
27 | Menú 32 |
33 |
34 | 35 | ); 36 | }; 37 | 38 | export default FormHeader; 39 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Felix Hertlein 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /api/src/utils/errorConstants.ts: -------------------------------------------------------------------------------- 1 | type ErrorCode = { 2 | status: number; 3 | message: string; 4 | }; 5 | 6 | export const ERROR_CODES: Record = { 7 | INCOMPLETE_DATA: { 8 | status: 400, 9 | message: 'Incomplete data. Please fill in all required fields.' 10 | }, 11 | UNAUTHORIZED_GENERAL: { 12 | status: 403, 13 | message: 'Access denied. General fiscal role required for this action.' 14 | }, 15 | RESOURCE_NOT_FOUND: { 16 | status: 404, 17 | message: 'Resource not found. Please verify the provided data.' 18 | }, 19 | INSTITUTION_NOT_FOUND: { 20 | status: 404, 21 | message: "Fiscal's institution not found." 22 | }, 23 | INVALID_MESA_OR_ESCUELA: { 24 | status: 404, 25 | message: 'Invalid mesa or school. Verify that the mesa belongs to the indicated school.' 26 | }, 27 | INTERNAL_SERVER_ERROR: { 28 | status: 500, 29 | message: 'Internal server error. Please try again later.' 30 | }, 31 | S3_UPLOAD_ERROR: { 32 | status: 500, 33 | message: "Error registering the report in S3." 34 | }, 35 | // Add more errors as needed 36 | }; 37 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/README.md: -------------------------------------------------------------------------------- 1 | # Alineamiento de imagenes de comprobantes de fiscales y telegramas de mesa 2 | 3 | Este script permite alinear automáticamente las fotografías de comprobantes de fiscales y telegramas de mesa, para facilitar el reconocimiento automático de caracteres. 4 | 5 | Basado en https://github.com/FelixHertlein/inv3d-model. Más detalles en https://github.com/Gissio/safe-ocr-alignment. 6 | 7 | ## Uso 8 | 9 | python align.py [IMAGE-FILE] [TEMPLATE-FILE] 10 | 11 | En el primer uso, descarga automáticamente el modelo de machine learning. 12 | 13 | El archivo de salida es `output/unwarped.png`. 14 | 15 | El tamaño del archivo de salida puede controlarse en `align.py`, en la línea: 16 | 17 | output_shape=(1400, 860), 18 | 19 | ## Ejemplos 20 | 21 | python align.py examples/00001.jpg examples/template1.png 22 | python align.py examples/00002.jpg examples/template1.png 23 | python align.py examples/00003.jpg examples/template1.png 24 | 25 | ## Tareas para hacer 26 | 27 | * Ajustar el contraste automáticamente para mejorar la precisión. 28 | * Optimizar la conversión por batches de imágenes. 29 | * Reconocer automáticamente la plantilla. 30 | -------------------------------------------------------------------------------- /frontend/src/components/selector/Selector.tsx: -------------------------------------------------------------------------------- 1 | import { Select, SelectContent, SelectItem, SelectTrigger } from './Exports'; 2 | 3 | interface ISelectorProps { 4 | placeholder: string; 5 | options: { key: string; label: string }[]; 6 | value?: string; 7 | onChange?: (value: string) => void; 8 | } 9 | 10 | function Selector({ placeholder, options, value, onChange }: ISelectorProps) { 11 | return ( 12 | 32 | ); 33 | } 34 | 35 | export default Selector; 36 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/eye-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /api/src/clients/resultadosApi/types.ts: -------------------------------------------------------------------------------- 1 | import {EleccionTipo, RecuentoTipo} from "./enums"; 2 | 3 | interface GetResultadosParamsRequest { 4 | anioEleccion: string 5 | tipoRecuento: RecuentoTipo 6 | tipoEleccion: EleccionTipo 7 | categoriaId: string 8 | distritoId?: string 9 | seccionProvincialId?: string 10 | seccionId?: string 11 | circuitoId?: string 12 | mesaId?: string 13 | } 14 | 15 | interface EstadoRecuento { 16 | mesasEsperadas: number 17 | mesasTotalizadas: number 18 | mesasTotalizadasPorcentaje: number 19 | cantidadElectores: number 20 | cantidadVotantes: number 21 | participacionPorcentaje: any 22 | } 23 | 24 | interface ValoresTotalizadosOtros { 25 | votosNulos: number 26 | votosNulosPorcentaje: any 27 | votosEnBlanco: number 28 | votosEnBlancoPorcentaje: any 29 | votosRecurridosComandoImpugnados: number 30 | votosRecurridosComandoImpugnadosPorcentaje: any 31 | } 32 | 33 | interface GetResultadosResponse { 34 | fechaTotalizacion: string 35 | estadoRecuento: EstadoRecuento 36 | valoresTotalizadosPositivos: any[] 37 | valoresTotalizadosOtros: ValoresTotalizadosOtros 38 | } 39 | 40 | export { 41 | GetResultadosParamsRequest, 42 | GetResultadosResponse, 43 | } -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node -r dotenv/config src/index.ts", 8 | "dev": "ts-node-dev --respawn --transpile-only -r dotenv/config src/index.ts", 9 | "test:examples": "ts-node -r dotenv/config src/examples/index.ts", 10 | "build:swagger": "tsc && node -r dotenv/config dist/src/generators/swagger.js" 11 | }, 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.438.0", 14 | "@aws-sdk/client-s3": "^3.438.0", 15 | "@aws-sdk/client-sqs": "^3.438.0", 16 | "axios": "^1.6.0", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.3.1", 19 | "express": "^4.18.2", 20 | "express-fileupload": "^1.4.1", 21 | "fs-extra": "^11.1.1", 22 | "multer": "^1.4.5-lts.1", 23 | "swagger-autogen": "^2.23.7", 24 | "swagger-ui-express": "^5.0.0", 25 | "winston": "3.11.0" 26 | }, 27 | "devDependencies": { 28 | "@types/cors": "^2.8.15", 29 | "@types/express": "^4.17.20", 30 | "@types/fs-extra": "^11.0.3", 31 | "@types/multer": "^1.4.9", 32 | "@types/node": "^20.8.9", 33 | "@types/swagger-ui-express": "^4.1.5", 34 | "ts-node": "^10.9.1", 35 | "ts-node-dev": "^2.0.0", 36 | "typescript": "^5.2.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/mask.py: -------------------------------------------------------------------------------- 1 | import math 2 | from typing import * 3 | 4 | import numpy as np 5 | 6 | from skimage import img_as_bool 7 | from skimage.transform import resize 8 | 9 | from .misc import check_tensor 10 | 11 | def scale_mask(mask: np.ndarray, resolution: Union[int, Tuple[int, int]] = None, area: int = None) -> np.ndarray: 12 | check_tensor(mask, "h w") 13 | 14 | assert not (bool(resolution) and bool(area)), "Both scaling values cannot be set!" 15 | 16 | if not resolution and not area: # no scaling parameter set: do nothing 17 | return mask 18 | 19 | if resolution: 20 | new_shape = resolution[::-1] if isinstance(resolution, Tuple) else (resolution, resolution) 21 | else: 22 | H, W = mask.shape 23 | new_W = int(math.sqrt(area / (H / W))) 24 | new_H = int(area / new_W) 25 | new_shape = (new_W, new_H) 26 | 27 | output_mask = img_as_bool(resize(mask, new_shape)) 28 | 29 | return output_mask 30 | 31 | 32 | def tight_crop_mask(mask: np.ndarray, return_mask: bool = False): 33 | check_tensor(mask, "h w") 34 | 35 | rows = np.any(mask, axis=1) 36 | cols = np.any(mask, axis=0) 37 | row_min, row_max = np.where(rows)[0][[0, -1]] 38 | col_min, col_max = np.where(cols)[0][[0, -1]] 39 | 40 | mask_crop = mask[row_min:row_max, col_min:col_max] 41 | 42 | return mask_crop 43 | -------------------------------------------------------------------------------- /api/src/clients/resultadosApi/enums.ts: -------------------------------------------------------------------------------- 1 | enum EleccionTipo { 2 | PASO = '1', 3 | GENERAL = '2', 4 | SEGUNDA_VUELTA = '3' 5 | } 6 | 7 | enum RecuentoTipo { 8 | PROVISORIO = '1', 9 | DEFINITIVO = '2', 10 | } 11 | 12 | enum PadronTipo { 13 | NORMAL = '1', 14 | COMANDO = '2', 15 | PRIVADOS_DE_LA_LIBERTAD = '3', 16 | RESIDENTES_EN_EL_EXTERIOR = '4', 17 | } 18 | 19 | enum MesaTipo { 20 | NATIVOS = '1', 21 | EXTRANJEROS = '2', 22 | } 23 | 24 | enum VotosTipo { 25 | POSITIVO = '1', 26 | EN_BLANCO = '2', 27 | IMPUGNADO = '3', 28 | RECURRIDO = '4', 29 | NULO = '5', 30 | COMANDO = '6', 31 | } 32 | 33 | enum Provincia { 34 | CABA = '1', 35 | BUENOS_AIRES = '2', 36 | CATAMARCA = '3', 37 | CORDOBA = '4', 38 | CORRIENTES = '5', 39 | CHACO = '6', 40 | CHUBUT = '7', 41 | ENTRE_RIOS = '8', 42 | FORMOSA = '9', 43 | JUJUY = '10', 44 | LA_PAMPA = '11', 45 | LA_RIOJA = '12', 46 | MENDOZA = '13', 47 | MISIONES = '14', 48 | NEUQUEN = '15', 49 | RIO_NEGRO = '16', 50 | SALTA = '17', 51 | SAN_JUAN = '18', 52 | SAN_LUIS = '19', 53 | SANTA_CRUZ = '20', 54 | SANTA_FE = '21', 55 | SANTIAGO_DEL_ESTERO = '22', 56 | TUCUMAN = '23', 57 | TIERRA_DEL_FUEGO = '24' 58 | } 59 | 60 | export { 61 | EleccionTipo, 62 | RecuentoTipo, 63 | PadronTipo, 64 | MesaTipo, 65 | VotosTipo, 66 | Provincia, 67 | } -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # Descripcion 2 | 3 | Base de api en nodejs express, contiene un [swagger ui endpoint](http://localhost:3000/api-docs) con un open API spec generado a partir de la definición del MVP. 4 | 5 | Ref: [MVP](https://docs.google.com/document/d/11F_YE7d1th6ORO_AVKZn9idoiMjSzepwSYcMBZONDt8) 6 | 7 | # Requirements 8 | 9 | [Node.js (v18+)](https://nodejs.org/en/download) 10 | [Docker](https://www.docker.com/products/docker-desktop/) 11 | [Yarn](https://yarnpkg.com/getting-started/install) 12 | [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) [Para Mac: brew install awscli] 13 | 14 | # Setup Instructions 15 | 16 | Para el desarrollo local, el proyecto emula los servicios de AWS con localstack. Por lo que no tenes que instalar ni configurar AWS CLI; incluso las variables `.env` se popularan automáticamente para que solo tengas que concentrarte en el desarrollo :). 17 | 18 | ```bash 19 | cd api && make start 20 | ``` 21 | 22 | ## AWS SDK Examples 23 | 24 | Una vez que esté corriendo localstack, podes mirar ejemplos de peticiones con el SDK de AWS en [ejemplos](examples/index.js). 25 | 26 | O simplemente corré `npm run test:examples` para verificar que todo funcione correctamente. 27 | 28 | # Swagger UI 29 | 30 | Corre automáticamente cuando arranca la app, en este [endpoint](http://localhost:3000/api-docs). 31 | 32 | ## Generate 33 | 34 | Corré 35 | 36 | ```bash 37 | yarn build:swagger 38 | ``` 39 | -------------------------------------------------------------------------------- /frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/model_factory.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Type, List 3 | from pytorch_lightning import LightningModule 4 | 5 | _all_models: dict[str, type[LightningModule]] = {} 6 | 7 | 8 | def get_all_models() -> List[str]: 9 | return sorted(list(_all_models.keys())) 10 | 11 | 12 | def register_model(name: str, model_class: type[LightningModule]): 13 | _all_models[name] = model_class 14 | 15 | 16 | def class_by_name(name: str) -> type[LightningModule]: 17 | if name not in _all_models: 18 | raise ValueError(f"Model '{name}' is unknown! Cannot find class!") 19 | 20 | return _all_models[name] 21 | 22 | 23 | def create_new(name: str, **model_kwargs) -> LightningModule: 24 | if name not in _all_models: 25 | raise ValueError(f"Model '{name}' is unknown! Cannot create a new instance!") 26 | 27 | return _all_models[name](**model_kwargs) 28 | 29 | 30 | def load_from_checkpoint(name: str, checkpoint_file: Path) -> LightningModule: 31 | if name not in _all_models: 32 | raise ValueError( 33 | f"Model '{name}' is unknown! Cannot load the model from checkpoint!" 34 | ) 35 | 36 | if not checkpoint_file.is_file(): 37 | raise ValueError( 38 | f"Model checkpoint '{str(checkpoint_file.resolve())}' does not exist! Cannot load the model from checkpoint!" 39 | ) 40 | 41 | return _all_models[name].load_from_checkpoint(str(checkpoint_file)) 42 | -------------------------------------------------------------------------------- /frontend/src/mocks/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "auth": { 3 | "dni": "12312312", 4 | "password": "1231231" 5 | }, 6 | "user": { 7 | "id": 1, 8 | "firstName": "Javier Gerardo", 9 | "lastName": "Milei", 10 | "email": "javiergerardomilei@gmail.com", 11 | "dni": "39977777", 12 | "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", 13 | "address": "Calle Falsa 123, Cordoba, Cordoba", 14 | "role": "admin", 15 | "province": "Buenos Aires", 16 | "circuit": "Circuito 1", 17 | "table": "Mesa 1" 18 | }, 19 | "refreshToken": { 20 | "id": 1, 21 | "firstName": "Javier Gerardo", 22 | "lastName": "Milei", 23 | "email": "javiergerardomilei@gmail.com", 24 | "dni": "39977777", 25 | "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", 26 | "address": "Calle Falsa 123, Cordoba, Cordoba", 27 | "role": "admin", 28 | "province": "Buenos Aires", 29 | "circuit": "Circuito 1", 30 | "table": "Mesa 1" 31 | }, 32 | "users": [ 33 | { 34 | "id": 1, 35 | "name": "Juan Pérez", 36 | "email": "juan@perez.com" 37 | }, 38 | { 39 | "id": 2, 40 | "name": "María Gómez", 41 | "email": "maria@gomez.com" 42 | }, 43 | { 44 | "id": 3, 45 | "name": "Carlos López", 46 | "email": "carlos@lopez.com" 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /frontend/src/components/loadingIndicator/index.tsx: -------------------------------------------------------------------------------- 1 | import { ILoadingIndicatorProps } from "./types"; 2 | 3 | export const LoadingIndicator: React.FC = ({ className = 'fill-white' }) => { 4 | return ( 5 |
6 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /api/src/controllers/upload.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import { UploadedFile } from 'express-fileupload'; 3 | import path from 'path'; 4 | 5 | export const uploadFile = async (req: Request, res: Response): Promise => { 6 | if (!req.files || Object.keys(req.files).length === 0) { 7 | return res.status(400).json({ message: 'No files were uploaded.' }); 8 | } 9 | 10 | const file: UploadedFile = req.files.file as UploadedFile; 11 | 12 | // Validate file type 13 | const allowedTypes: string[] = ['image/jpeg', 'image/png', 'image/gif']; 14 | if (!allowedTypes.includes(file.mimetype)) { 15 | return res.status(400).json({ message: 'Invalid file type.' }); 16 | } 17 | 18 | // Validate file size (e.g., 5MB) 19 | const maxSize: number = 5 * 1024 * 1024; // 5MB in bytes 20 | if (file.size > maxSize) { 21 | return res.status(400).json({ message: 'File size exceeds limit.' }); 22 | } 23 | 24 | // Save the file locally 25 | const fileName: string = file.name; 26 | const uploadPath: string = path.join(__dirname, '..', 'public', 'uploads', fileName); 27 | 28 | try { 29 | await new Promise((resolve, reject) => { 30 | file.mv(uploadPath, (err: Error | null) => { 31 | if (err) { 32 | reject(err); 33 | } else { 34 | resolve(); 35 | } 36 | }); 37 | }); 38 | 39 | return res.status(200).json({ message: 'File uploaded successfully.', fileName: fileName, filePath: uploadPath }); 40 | } catch (err) { 41 | return res.status(500).json({ message: 'Error uploading file', error: err }); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /api/src/types/models.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: string 3 | roles: string[] 4 | whatsapp: string 5 | mesa_id: string 6 | } 7 | 8 | export interface Fiscalizar { 9 | imagen: string 10 | validado: boolean 11 | errores: boolean 12 | observaciones: string 13 | } 14 | 15 | export interface Session { 16 | userId: string 17 | } 18 | 19 | // Interfaz para representar una Mesa 20 | export interface Mesa { 21 | id: string; 22 | numero: number; 23 | escuelaId: string; // Referencia a la escuela asociada 24 | } 25 | 26 | // Interfaz para representar una Escuela 27 | export interface Escuela { 28 | id: string; 29 | nombre: string; 30 | direccion: string; 31 | } 32 | 33 | // Interfaz para el reporte de falta de fiscales 34 | export interface ReportFaltaFiscal { 35 | id: string; // ID único para el reporte 36 | fiscalId: string; // ID del fiscal que reporta 37 | mesaId: string; // ID de la mesa donde falta el fiscal 38 | escuelaId: string; // ID de la escuela asociada a la mesa 39 | timestamp: Date; // Fecha y hora del reporte 40 | observaciones: string; // Observaciones adicionales si son necesarias 41 | } 42 | 43 | interface ResultadoSubidaS3 { 44 | key: string; 45 | bucket: string; 46 | status: string; 47 | resultadoMetadata: unknown; // Aquí puedes especificar un tipo más preciso si lo conoces 48 | } 49 | 50 | interface ErrorSubidaS3 { 51 | error: true; 52 | mensaje: string; 53 | detalles: string; 54 | codigoError: number; 55 | } 56 | 57 | // Tipo de retorno unificado que puede ser uno de los dos anteriores 58 | export type ResultadoRegistroS3 = ResultadoSubidaS3 | ErrorSubidaS3; 59 | -------------------------------------------------------------------------------- /frontend/src/components/dataProfile/index.tsx: -------------------------------------------------------------------------------- 1 | import { IUser } from '#/interfaces/IUser'; 2 | import { 3 | IFieldProps, 4 | IProfileDataProps, 5 | IProfileDataTableProps, 6 | } from './types'; 7 | 8 | export const DataProfile = ({ user }: IProfileDataProps) => { 9 | const profileData: IProfileDataTableProps[] = [ 10 | { title: 'Nombres', text: user.firstName }, 11 | { title: 'Apellido', text: user.lastName }, 12 | { title: 'Email', text: user.email }, 13 | { title: 'DNI', text: user.dni }, 14 | { title: 'Provincia', text: user.province }, 15 | { title: 'Circuito', text: user.circuit }, 16 | { title: 'Mesa', text: user.table }, 17 | ]; 18 | 19 | return ( 20 |
21 | {profileData?.map((fieldText, index) => ( 22 | 27 | ))} 28 |
29 | ); 30 | }; 31 | 32 | export const FieldText = ({ fieldText, isLast }: IFieldProps) => { 33 | return ( 34 |
39 |
40 | {fieldText.title} 41 |
42 | 43 |
44 | {fieldText.text} 45 |
46 |
47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/useAxios.tsx: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse, AxiosError, Method } from 'axios'; 2 | 3 | const API_URL = 'http://localhost:5000'; 4 | 5 | type T = any; 6 | 7 | interface FetchResponse { 8 | data?: T; 9 | error?: AxiosError; 10 | loading: boolean; 11 | } 12 | 13 | const useAxios = () => { 14 | const fetchData = async ( 15 | method: Method, 16 | url: string, 17 | body?: any, 18 | ): Promise> => { 19 | let responseState: FetchResponse = { 20 | loading: true, 21 | data: undefined, 22 | error: undefined, 23 | }; 24 | 25 | try { 26 | const response: AxiosResponse = await axios({ 27 | method, 28 | url: `${API_URL}${url}`, 29 | data: body, 30 | }); 31 | 32 | if (response.status >= 400) throw new Error(response.statusText); 33 | 34 | responseState = { 35 | ...responseState, 36 | data: response.data, 37 | loading: false, 38 | }; 39 | } catch (error) { 40 | responseState = { 41 | ...responseState, 42 | loading: false, 43 | error: error as AxiosError, 44 | }; 45 | } 46 | 47 | return responseState; 48 | }; 49 | 50 | const get = (url: string) => fetchData('GET', url); 51 | const post = (url: string, body: any) => fetchData('POST', url, body); 52 | const put = (url: string, body: any) => fetchData('PUT', url, body); 53 | const del = (url: string) => fetchData('DELETE', url); 54 | 55 | return { 56 | get, 57 | post, 58 | put, 59 | delete: del, 60 | }; 61 | }; 62 | 63 | export default useAxios; 64 | -------------------------------------------------------------------------------- /frontend/src/components/progressIndicator/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { IProgressIndicatorProps, ProgressStepStatus } from './types'; 3 | import './styles.css'; 4 | 5 | const ProgressIndicator = ({ steps }: IProgressIndicatorProps) => { 6 | return ( 7 |
8 | {steps.map((step, index) => ( 9 | 10 |
19 | {step === ProgressStepStatus.Successful ? ( 20 | 25 | ) : ( 26 | 27 | {(index + 1).toString()} 28 | 29 | )} 30 |
31 | 32 | {index != steps.length - 1 && ( 33 |
42 | )} 43 |
44 | ))} 45 |
46 | ); 47 | }; 48 | 49 | export default ProgressIndicator; 50 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/parallel.py: -------------------------------------------------------------------------------- 1 | import concurrent.futures 2 | import math 3 | import multiprocessing 4 | import traceback 5 | from pathlib import Path 6 | from typing import * 7 | 8 | from tqdm import tqdm 9 | 10 | 11 | def process_tasks(task_fn: Callable, tasks: List[Any], num_workers: int, use_indexes: bool = False, **kwargs) -> Dict: 12 | print("Starting parallel execution with {} workers!".format(num_workers)) 13 | 14 | with concurrent.futures.ProcessPoolExecutor(max_workers=num_workers) as executor: 15 | futures = {executor.submit(task_fn, task, **kwargs): idx if use_indexes else task for idx, task in enumerate(tasks)} 16 | results = {} 17 | 18 | print("Awaiting completion!".format(num_workers)) 19 | 20 | try: 21 | with tqdm(desc="Processing ...", total=len(futures), smoothing=0) as progress_bar: 22 | for f in concurrent.futures.as_completed(futures.keys()): 23 | try: 24 | results[futures[f]] = f.result() 25 | except Exception: 26 | print("EXCEPTION: ", traceback.format_exc()) 27 | print(f"FAILED TASK: {futures[f]}") 28 | progress_bar.update(1) 29 | except KeyboardInterrupt: 30 | executor.shutdown(wait=False) 31 | exit(-1) 32 | 33 | return results 34 | 35 | def get_cpus(): 36 | try: 37 | cfs_quota_us = int(Path("/sys/fs/cgroup/cpu/cpu.cfs_quota_us").read_text()) 38 | cfs_period_us = int(Path("/sys/fs/cgroup/cpu/cpu.cfs_period_us").read_text()) 39 | if cfs_quota_us > 0 and cfs_period_us > 0: 40 | return int(math.ceil(cfs_quota_us / cfs_period_us)) 41 | except: 42 | pass 43 | return multiprocessing.cpu_count() 44 | -------------------------------------------------------------------------------- /Fuentes_de_datos/descargador_de_telegramas/descargador.php: -------------------------------------------------------------------------------- 1 | 'https://resultados.gob.ar/backend-difu/scope/data/getTiff/'.$mesa, 22 | CURLOPT_RETURNTRANSFER => true, 23 | CURLOPT_ENCODING => '', 24 | CURLOPT_MAXREDIRS => 10, 25 | CURLOPT_TIMEOUT => 0, 26 | CURLOPT_HTTPHEADER => array( 27 | 'Accept: */*', 28 | 'Connection: keep-alive', 29 | 'Accept-Encoding: gzip, deflate, br', 30 | 'User-Agent: PostmanRuntime/7.33.0', 31 | ), 32 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 33 | CURLOPT_CUSTOMREQUEST => 'GET', 34 | )); 35 | 36 | $response = curl_exec($curl); 37 | $responseParsed = json_decode($response); 38 | curl_close($curl); 39 | return $responseParsed->encodingBinary; 40 | } 41 | 42 | function base64_to_jpeg( $base64_string, $output_file ) { 43 | $status = file_put_contents($output_file, base64_decode($base64_string)); 44 | chmod($output_file,777); 45 | return($status); 46 | } 47 | 48 | 49 | ?> -------------------------------------------------------------------------------- /frontend/src/pages/loading-page/loadingPage.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingIndicator } from '#/components/loadingIndicator'; 2 | 3 | export const LoadingPage: React.FC = () => { 4 | return ( 5 |
6 |
7 |
8 |
9 | fenix 14 | lla 19 |
20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 |
33 | 34 | {/* 35 | // TODO: FIX FOOTER IMAGE DESIGN 36 | // https://www.figma.com/file/iO7j93Rxbk2nIfYdqpAmv2/%F0%9F%A6%85-APP-Fiscalizaci%C3%B3n-Libertaria-%7C-%F0%9F%93%B1-FINAL?type=design&node-id=59-4193&mode=dev 37 |
footer 42 |
43 | */} 44 |
45 | ); 46 | }; 47 | 48 | export default LoadingPage; 49 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/mail-contested.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/components/flatList/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { FlatListProps } from './types'; 3 | 4 | const FlatList = ({ 5 | logo, 6 | type, 7 | subTitle, 8 | title, 9 | votes, 10 | edit = false, 11 | updateTotalVotes, 12 | }: FlatListProps) => { 13 | const [vote, setVote] = useState(votes); 14 | 15 | const handleVoteChange = (value: number) => { 16 | const newValue: number = value; 17 | if (newValue >= 0) { 18 | setVote(newValue); 19 | updateTotalVotes(newValue - vote); 20 | } 21 | }; 22 | 23 | const titleColor: any = { 24 | massa: 'text-sky-400', 25 | milei: 'text-violet-800', 26 | null: 'text-neutral-500', 27 | appealed: 'text-neutral-500', 28 | contested: 'text-neutral-500', 29 | electoralCommand: 'text-neutral-500', 30 | blank: 'text-neutral-500', 31 | }; 32 | 33 | const selectedInputStyle: string | null = 34 | vote > 0 ? 'border-2 border-violet-brand !text-black' : null; 35 | 36 | return ( 37 |
38 | logo 39 |
40 | 45 | 50 |
51 | ) => handleVoteChange(Number(e.target.value))} 54 | value={vote} 55 | readOnly={!edit} 56 | className={`border-2 text-center border-gray-300 outline-none cursor-default bg-white text-neutral-700 font-bold rounded-xl h-12 w-32 flex text-2xl ${selectedInputStyle}`} 57 | /> 58 |
59 | ); 60 | }; 61 | 62 | export default FlatList; 63 | -------------------------------------------------------------------------------- /frontend/src/context/AuthContext.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createContext, 3 | useContext, 4 | ReactNode, 5 | useState, 6 | useEffect, 7 | } from 'react'; 8 | import useAxios from '#/hooks/utils/useAxios'; 9 | import { IUser } from '#/interfaces/IUser'; 10 | import { useNavigate } from 'react-router-dom'; 11 | 12 | interface AuthContextType { 13 | isAuthenticated: boolean; 14 | user: IUser | null; 15 | login: (user: IUser) => void; 16 | logout: () => void; 17 | } 18 | 19 | const AuthContext = createContext(undefined); 20 | 21 | interface AuthProviderProps { 22 | children: ReactNode; 23 | } 24 | 25 | export const AuthProvider: React.FC = ({ children }) => { 26 | const axios = useAxios(); 27 | const navigate = useNavigate(); 28 | const [isAuthenticated, setIsAuthenticated] = useState(false); 29 | const [user, setUser] = useState(null); 30 | 31 | useEffect(() => { 32 | refreshToken(); 33 | }, []); 34 | 35 | const login = (userData: IUser) => { 36 | sessionStorage.setItem('accessToken', userData.jwt); 37 | setIsAuthenticated(true); 38 | setUser(userData); 39 | }; 40 | 41 | const logout = () => { 42 | sessionStorage.removeItem('accessToken'); 43 | setIsAuthenticated(false); 44 | setUser(null); 45 | navigate('/login'); 46 | }; 47 | 48 | const refreshToken = async () => { 49 | const accessToken = sessionStorage.getItem('accessToken'); 50 | if (!accessToken) return logout(); 51 | 52 | const { data, error } = await axios.get('/refreshToken'); 53 | if (error) return logout(); 54 | 55 | login(data); 56 | }; 57 | 58 | return ( 59 | 60 | {children} 61 | 62 | ); 63 | }; 64 | 65 | export const useAuth = () => { 66 | const context = useContext(AuthContext); 67 | if (!context) { 68 | throw new Error('useAuth debe ser usado dentro de un AuthProvider'); 69 | } 70 | return context; 71 | }; 72 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import react from "@vitejs/plugin-react-swc"; 4 | import { defineConfig } from "vite"; 5 | import autoAlias from "vite-plugin-auto-alias"; 6 | import dynamicImport from "vite-plugin-dynamic-import"; 7 | import { ViteImageOptimizer } from "vite-plugin-image-optimizer"; 8 | import { ViteMinifyPlugin } from "vite-plugin-minify"; 9 | import { viteObfuscateFile } from "vite-plugin-obfuscator"; 10 | 11 | const obfuscator_options = { 12 | compact: true, 13 | controlFlowFlattening: false, 14 | deadCodeInjection: false, 15 | debugProtection: false, 16 | debugProtectionInterval: 0, 17 | disableConsoleOutput: false, 18 | identifierNamesGenerator: "hexadecimal", 19 | log: false, 20 | numbersToExpressions: false, 21 | renameGlobals: false, 22 | selfDefending: false, 23 | simplify: true, 24 | splitStrings: false, 25 | stringArray: true, 26 | stringArrayCallsTransform: false, 27 | stringArrayCallsTransformThreshold: 0.5, 28 | stringArrayEncoding: [], 29 | stringArrayIndexShift: true, 30 | stringArrayRotate: true, 31 | stringArrayShuffle: true, 32 | stringArrayWrappersCount: 1, 33 | stringArrayWrappersChainedCalls: true, 34 | stringArrayWrappersParametersMaxCount: 2, 35 | stringArrayWrappersType: "variable", 36 | stringArrayThreshold: 0.01, 37 | unicodeEscapeSequence: false, 38 | }; 39 | 40 | export default defineConfig({ 41 | plugins: [ 42 | react(), 43 | autoAlias({ 44 | mode: "sync", 45 | prefix: "#", 46 | }), 47 | 48 | ViteImageOptimizer(), 49 | ViteMinifyPlugin({}), 50 | viteObfuscateFile(obfuscator_options), 51 | dynamicImport(), 52 | ], 53 | build: { 54 | target: "esnext", 55 | chunkSizeWarningLimit: 600, 56 | rollupOptions: { 57 | output: { 58 | manualChunks: (id) => { 59 | if (id.includes("node_modules")) 60 | return id 61 | .toString() 62 | .split("node_modules/")[1] 63 | .split("/")[0] 64 | .toString(); 65 | }, 66 | }, 67 | }, 68 | }, 69 | }); 70 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend-fiscales", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "concurrently \"vite\" \"npm run server\"", 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "preview": "tsc && vite build && vite preview", 12 | "preview-stg": "tsc && vite build --mode staging && vite preview", 13 | "server": "json-server --watch ./src/mocks/db.json --port 5000" 14 | }, 15 | "dependencies": { 16 | "@radix-ui/react-icons": "^1.3.0", 17 | "@radix-ui/react-select": "^2.0.0", 18 | "@types/react-router-dom": "^5.3.3", 19 | "axios": "^1.5.0", 20 | "class-variance-authority": "^0.7.0", 21 | "classnames": "^2.3.2", 22 | "clsx": "^2.0.0", 23 | "formik": "^2.4.3", 24 | "mitt": "^3.0.1", 25 | "mobx": "^6.10.2", 26 | "mobx-react": "^9.0.1", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0", 29 | "react-hot-toast": "^2.4.1", 30 | "react-router-dom": "^6.17.0", 31 | "tailwind-merge": "^2.0.0", 32 | "tailwindcss-animate": "^1.0.7", 33 | "yup": "^1.2.0" 34 | }, 35 | "devDependencies": { 36 | "@types/react": "^18.2.15", 37 | "@types/react-dom": "^18.2.7", 38 | "@typescript-eslint/eslint-plugin": "^6.0.0", 39 | "@typescript-eslint/parser": "^6.0.0", 40 | "@vitejs/plugin-react-swc": "^3.3.2", 41 | "autoprefixer": "^10.4.15", 42 | "concurrently": "^8.2.2", 43 | "eslint": "^8.45.0", 44 | "eslint-config-prettier": "^9.0.0", 45 | "eslint-plugin-prettier": "^5.0.1", 46 | "eslint-plugin-react-hooks": "^4.6.0", 47 | "eslint-plugin-react-refresh": "^0.4.3", 48 | "json-server": "^0.17.4", 49 | "prettier": "^3.0.3", 50 | "tailwindcss": "^3.3.3", 51 | "typescript": "^5.0.2", 52 | "vite": "^4.4.5", 53 | "vite-plugin-auto-alias": "^0.2.11", 54 | "vite-plugin-dynamic-import": "^1.5.0", 55 | "vite-plugin-image-optimizer": "^1.1.7", 56 | "vite-plugin-minify": "^1.5.2", 57 | "vite-plugin-obfuscator": "^1.0.5" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /api/src/utils/s3Utils.ts: -------------------------------------------------------------------------------- 1 | import { PutObjectCommand, PutObjectCommandOutput, S3Client } from '@aws-sdk/client-s3'; 2 | import { ReportFaltaFiscal } from '../types/models'; 3 | import { ResultadoRegistroS3 } from '../types/models'; 4 | import { ERROR_CODES } from './errorConstants'; 5 | 6 | type S3Config = { 7 | region: string; 8 | } 9 | 10 | type S3UploadParams = { 11 | Bucket: string; 12 | Key: string; 13 | Body: string; 14 | } 15 | 16 | // Inicializa el cliente de S3 con las configuraciones necesarias 17 | const s3Config: S3Config = { region: 'tu-region' }; 18 | const s3Client: S3Client = new S3Client(s3Config); 19 | 20 | /** 21 | * Sube un reporte al bucket S3 especificado. 22 | * 23 | * @param {ReportFaltaFiscal} reporte - El objeto reporte que quieres subir. 24 | * @return {Promise} - El resultado de la operación de subida. 25 | */ 26 | export async function registrarReporteEnS3(reporte: ReportFaltaFiscal): Promise { 27 | const bucketName: string = 'nombre-de-tu-bucket'; 28 | const objectKey: string = `reportes/${reporte.fiscalId}-${new Date().getTime()}.json`; 29 | 30 | const uploadParams: S3UploadParams = { 31 | Bucket: bucketName, 32 | Key: objectKey, 33 | Body: JSON.stringify(reporte), 34 | }; 35 | 36 | const comando: PutObjectCommand = new PutObjectCommand(uploadParams); 37 | 38 | try { 39 | const resultado: PutObjectCommandOutput = await s3Client.send(comando); 40 | // Devuelve la información relevante sobre la operación de subida 41 | return { 42 | key: objectKey, 43 | bucket: bucketName, 44 | status: 'Subido con éxito', 45 | resultadoMetadata: resultado.$metadata 46 | }; 47 | } catch (error) { 48 | console.error('Error al subir a S3:', error); 49 | // Devuelve un objeto con detalles del error para un mejor manejo 50 | return { 51 | error: true, 52 | mensaje: ERROR_CODES.S3_UPLOAD_ERROR.message, 53 | codigoError: ERROR_CODES.S3_UPLOAD_ERROR.status, 54 | detalles: error.message || 'Error no especificado' 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/mail-question.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /frontend/src/pages/desk-data/DeskData.tsx: -------------------------------------------------------------------------------- 1 | import { Selector } from '#/components/selector'; 2 | import Button from '#/components/button'; 3 | import { observer } from 'mobx-react-lite'; 4 | import Navbar from '#/components/navbar'; 5 | import ProgressIndicator from '#/components/progressIndicator'; 6 | import FormHeader from '#/components/formHeader'; 7 | import { ProgressStepStatus } from '#/components/progressIndicator/types'; 8 | 9 | const DeskData = () => { 10 | const DummyData = ['Example', 'Example 1', 'Example 2']; 11 | 12 | return ( 13 | <> 14 |
15 | 16 | 17 |
18 | 26 |
27 | 28 |
29 |

Ubicacion de la mesa

30 |

31 | Recopilación de la ubicación precisa del{' '} 32 | centro educativo. 33 |

34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 |
49 |
50 | 51 | ); 52 | }; 53 | 54 | export const deskData = observer(DeskData); 55 | 56 | export default DeskData; 57 | -------------------------------------------------------------------------------- /frontend/src/routes/routes.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingPage } from '#/pages/loading-page'; 2 | import { lazy } from 'react'; 3 | import { Route, Routes } from 'react-router-dom'; 4 | 5 | const Login = lazy(() => import('#/pages/login/login')); 6 | const Profile = lazy(() => import('#/pages/profile/profile')); 7 | const SendSuccess = lazy(() => import('#/pages/send-success/sendSuccess')); 8 | const LoadInformation = lazy( 9 | () => import('#/pages/load-information/loadInformation'), 10 | ); 11 | const Dashboard = lazy(() => import('#/pages/dashboard/dashboard')); 12 | const UploadCertificate = lazy( 13 | () => import('#/pages/upload-certificate/uploadCertificate'), 14 | ); 15 | const VerifyCertificate = lazy( 16 | () => import('#/pages/verify-certificate/verifyCertificate'), 17 | ); 18 | const TotalResults = lazy(() => import('#/pages/total-results/totalResults')); 19 | const FilterPage = lazy(() => import('#/pages/results/filter')); 20 | const NotFound = lazy(() => import('#/pages/not-found/notFound')); 21 | const DeskData = lazy(() => import('#/pages/desk-data/DeskData')); 22 | 23 | const AppRoutes: React.FC = () => ( 24 | 25 | {/* Auth */} 26 | } /> 27 | {/* TODO: Agregar Middleware (si no existe user, no entra a las siguientes rutas) */} 28 | 29 | {/* Cuenta */} 30 | } /> 31 | } /> 32 | 33 | {/* Steps Formulario */} 34 | } /> 35 | } /> 36 | } /> 37 | } /> 38 | } /> 39 | 40 | {/* Filters & Results */} 41 | } /> 42 | } /> 43 | 44 | {/* Utils */} 45 | } /> 46 | } /> 47 | 48 | {/* 404 Not found */} 49 | } /> 50 | 51 | ); 52 | 53 | export default AppRoutes; 54 | -------------------------------------------------------------------------------- /api/src/examples/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import path from 'path' 3 | import { DynamoDBClient, GetItemCommand, PutItemCommand } from '@aws-sdk/client-dynamodb' 4 | import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3' 5 | import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs' 6 | 7 | const awsEndpoint = process.env.AWS_ENDPOINT 8 | const dynamodbClient = new DynamoDBClient({ endpoint: awsEndpoint }) 9 | const s3Client = new S3Client({ endpoint: awsEndpoint, forcePathStyle: true }) 10 | const sqsClient = new SQSClient({ endpoint: awsEndpoint }) 11 | 12 | async function insertDBItem(id: string, description: string) { 13 | const params = { 14 | TableName: process.env.DYNAMODB_TABLE_NAME, 15 | Item: { 16 | id: { S: id }, 17 | description: { S: description } 18 | } 19 | } 20 | const command = new PutItemCommand(params) 21 | const result = await dynamodbClient.send(command) 22 | console.log('DynamoDB insertItem success') 23 | } 24 | 25 | async function fetchDBItemById(id: string) { 26 | const params = { 27 | TableName: process.env.DYNAMODB_TABLE_NAME, 28 | Key: { 29 | id: { S: id } 30 | } 31 | } 32 | const command = new GetItemCommand(params) 33 | const result = await dynamodbClient.send(command) 34 | console.log(`DynamoDB fetchItemById success`) 35 | } 36 | 37 | async function uploadToS3(Key: string, filePath: string) { 38 | const file = await fs.readFile(path.resolve(__dirname, filePath)) 39 | const command = new PutObjectCommand({ 40 | Key, 41 | Body: file, 42 | Bucket: process.env.S3_REPORTS_BUCKET, 43 | ContentType: 'text/plain' 44 | }) 45 | const res = await s3Client.send(command) 46 | console.log(`uploadToS3 success`) 47 | } 48 | 49 | async function sendMessageToSQS(MessageBody: string) { 50 | const command = new SendMessageCommand({ QueueUrl: process.env.QUEUE_URL, MessageBody }) 51 | const res = await sqsClient.send(command) 52 | console.log(`sendMessageToSQS message sent`) 53 | } 54 | 55 | // test AWS services 56 | insertDBItem('12345', 'This is a test item').catch(console.log) 57 | fetchDBItemById('1234').catch(console.log) 58 | uploadToS3('assets', 'README.md').catch(console.log) 59 | sendMessageToSQS(JSON.stringify({ message: 'test message' })).catch(console.log) 60 | -------------------------------------------------------------------------------- /frontend/src/pages/send-success/sendSuccess.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { observer } from 'mobx-react'; 4 | import Button from '#/components/button'; 5 | import FormHeader from '#/components/formHeader'; 6 | import ProgressIndicator from '#/components/progressIndicator'; 7 | import { ProgressStepStatus } from '#/components/progressIndicator/types'; 8 | import { ISendSuccessProps } from './types'; 9 | import './styles.css'; 10 | 11 | const SendSuccessPage: FC = ({ message }) => { 12 | return ( 13 |
14 | 15 |
16 |
17 |
18 | 26 |
27 |
28 | data sent successful 33 |
34 |
35 |

36 | {message ?? '¡ MUCHAS GRACIAS POR FISCALIZAR !'} 37 |

38 |
39 |
40 | {/* TODO: Mover a Dashboard */} 41 | 42 |
49 |
50 |
51 |
52 | ); 53 | }; 54 | 55 | export const SendSuccess = observer(SendSuccessPage); 56 | 57 | export default SendSuccess; 58 | -------------------------------------------------------------------------------- /frontend/src/components/uploadImage/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import ImageInput from '#/components/imageInput'; 3 | import { getBase64 } from '#/utils'; 4 | 5 | export function UploadImage({ 6 | onUpload, 7 | }: { 8 | onUpload: (image: string) => void; 9 | }) { 10 | const [preview, setPreview] = useState(); 11 | async function onUploadInternal(file: File | null | undefined) { 12 | if (!file) return; 13 | const base64 = await getBase64(file); 14 | onUpload(base64); 15 | handlePreview(file); 16 | } 17 | 18 | function handlePreview(file: File) { 19 | const objectUrl: string = URL.createObjectURL(file); 20 | setPreview(objectUrl); 21 | } 22 | 23 | return ( 24 |
{ 27 | e.preventDefault(); 28 | }} 29 | onDrop={(e) => { 30 | e.preventDefault(); 31 | const file = e.dataTransfer.items[0].getAsFile(); 32 | onUploadInternal(file); 33 | }} 34 | > 35 |
36 | 53 |
54 | 65 |
66 | ); 67 | } 68 | 69 | export default UploadImage; 70 | -------------------------------------------------------------------------------- /frontend/src/components/snackbar/index.tsx: -------------------------------------------------------------------------------- 1 | import { toast, Toaster, ToastPosition } from 'react-hot-toast'; 2 | import { ToastiBarProps } from './types'; 3 | 4 | const toastiBar = ({ 5 | text, 6 | action, 7 | close, 8 | twoLine, 9 | textTwo, 10 | actionText, 11 | }: ToastiBarProps) => { 12 | let longerAction = false; 13 | 14 | if (actionText && actionText?.length > 7) { 15 | longerAction = true; 16 | } 17 | 18 | const config = { 19 | duration: close ? Infinity : 4000, 20 | position: 'top-center' as ToastPosition, 21 | }; 22 | 23 | toast.custom( 24 | (t) => ( 25 |
34 |
35 |

{text}

36 | {textTwo &&

{textTwo}

} 37 |
38 |
43 | {action && ( 44 |
49 |

action()}> 50 | {actionText} 51 |

52 |
53 | )} 54 | {close && ( 55 |
60 |

toast.dismiss(t.id)}> 61 | 66 |

67 |
68 | )} 69 |
70 |
71 | ), 72 | config, 73 | ); 74 | }; 75 | 76 | export const SnackBar = (props: ToastiBarProps) => { 77 | toastiBar(props); 78 | return ; 79 | }; 80 | -------------------------------------------------------------------------------- /frontend/src/assets/images/sad-face.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/mail-invalid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 13 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/geotr.py: -------------------------------------------------------------------------------- 1 | from .geotr_core import * 2 | import warnings 3 | 4 | import pytorch_lightning as pl 5 | import torch 6 | import torch.nn.functional as F 7 | from inv3d_util.misc import median_blur 8 | from torch.optim.lr_scheduler import OneCycleLR 9 | from einops import rearrange 10 | 11 | warnings.filterwarnings('ignore') 12 | 13 | 14 | class LitGeoTr(pl.LightningModule): 15 | dataset_options = { 16 | "resolution": 288, 17 | } 18 | 19 | train_options = { 20 | "max_epochs": 300, 21 | "batch_size": 8, 22 | "gradient_clip_val": 1, 23 | "early_stopping_patience": 25, 24 | } 25 | 26 | def __init__(self): 27 | super().__init__() 28 | self.model = GeoTr(num_attn_layers=6) 29 | self.epochs = None 30 | self.steps_per_epoch = None 31 | 32 | def forward(self, image, **kwargs): 33 | bm = self.model(image) 34 | bm = rearrange(bm, "b c h w -> b c w h") / 288 35 | bm = median_blur(bm) 36 | bm = torch.clamp(bm, min=0, max=1) 37 | return bm 38 | 39 | def training_step(self, batch, batch_idx): 40 | bm_true = rearrange(batch["train"]["bm"], "b c h w -> b c w h") * 288 41 | bm_pred = self.model(batch["input"]["image"]) 42 | 43 | l1_loss = F.l1_loss(bm_pred, bm_true) 44 | mse_loss = F.mse_loss(bm_pred, bm_true) 45 | 46 | self.log("train/l1_288_loss", l1_loss) 47 | self.log("train/mse_288_loss", mse_loss) 48 | 49 | return l1_loss 50 | 51 | def validation_step(self, batch, batch_idx): 52 | bm_true = batch["train"]["bm"] 53 | 54 | bm_pred = self.model(batch["input"]["image"]) 55 | bm_pred = rearrange(bm_pred, "b c h w -> b c w h") / 288 56 | 57 | self.log("val/mse_loss", F.mse_loss(bm_pred, bm_true), sync_dist=True) 58 | self.log("val/l1_loss", F.l1_loss(bm_pred, bm_true), sync_dist=True) 59 | 60 | def configure_optimizers(self): 61 | assert self.epochs is not None 62 | assert self.steps_per_epoch is not None 63 | 64 | optimizer = torch.optim.AdamW(self.parameters()) 65 | scheduler = OneCycleLR(optimizer, max_lr=10e-4, epochs=self.epochs, 66 | steps_per_epoch=self.steps_per_epoch) 67 | return { 68 | "optimizer": optimizer, 69 | "lr_scheduler": scheduler, 70 | "monitor": "val/mse_loss" 71 | } 72 | -------------------------------------------------------------------------------- /frontend/src/pages/dashboard/dashboard.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react-lite'; 2 | import { Link } from 'react-router-dom'; 3 | import Button from '#/components/button'; 4 | import Navbar from '#/components/navbar'; 5 | import { useAuth } from '#/context/AuthContext'; 6 | 7 | const DashboardPage = () => { 8 | const { user } = useAuth(); 9 | return ( 10 |
11 | 12 |
13 |
14 |
15 |

16 | ¡Bienvenido {user?.firstName} {user?.lastName}! 17 |

18 |

{user?.email}

19 |
20 |
21 | 22 |
48 |
49 |
50 |
51 | ); 52 | }; 53 | 54 | export const Dashboard = observer(DashboardPage); 55 | 56 | export default Dashboard; 57 | -------------------------------------------------------------------------------- /frontend/src/pages/upload-certificate/uploadCertificate.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | import ProgressIndicator from '#/components/progressIndicator'; 4 | import FormHeader from '#/components/formHeader'; 5 | import UploadImage from '#/components/uploadImage'; 6 | import { ProgressStepStatus } from '#/components/progressIndicator/types'; 7 | import './styles.css'; 8 | import { useNavigate } from 'react-router-dom'; 9 | 10 | const CheckItem = ({ text }: { text: string }) => ( 11 |
12 |
13 | 14 |
15 |

{text}

16 |
17 | ); 18 | 19 | const UploadCertificate = () => { 20 | const navigate = useNavigate(); 21 | // TODO: Replace with context useState 22 | const [certificateImage, setCertificateImage] = useState(); 23 | 24 | useEffect(() => { 25 | if (certificateImage) navigate('/verify-certificate'); 26 | }, [certificateImage]); 27 | 28 | return ( 29 |
30 | 31 |
32 |
33 | 41 |
42 |

43 | Usá la cámara para subir el certificado del fiscal, o 44 | cargala desde la galería. 45 |

46 |
47 | 48 | 49 | 53 |
54 |
55 |
56 | 57 |
58 |
59 | ); 60 | }; 61 | 62 | export default UploadCertificate; 63 | -------------------------------------------------------------------------------- /frontend/src/hooks/utils/use-event-listener.tsx: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect, useRef } from "react"; 2 | 3 | function useEventListener( 4 | eventName: K, 5 | handler: (event: MediaQueryListEventMap[K]) => void, 6 | element: RefObject, 7 | options?: boolean | AddEventListenerOptions 8 | ): void; 9 | 10 | function useEventListener( 11 | eventName: K, 12 | handler: (event: WindowEventMap[K]) => void, 13 | element?: undefined, 14 | options?: boolean | AddEventListenerOptions 15 | ): void; 16 | 17 | function useEventListener< 18 | K extends keyof HTMLElementEventMap, 19 | T extends HTMLElement = HTMLDivElement 20 | >( 21 | eventName: K, 22 | handler: (event: HTMLElementEventMap[K]) => void, 23 | element: RefObject, 24 | options?: boolean | AddEventListenerOptions 25 | ): void; 26 | 27 | function useEventListener( 28 | eventName: K, 29 | handler: (event: DocumentEventMap[K]) => void, 30 | element: RefObject, 31 | options?: boolean | AddEventListenerOptions 32 | ): void; 33 | 34 | function useEventListener< 35 | KW extends keyof WindowEventMap, 36 | KH extends keyof HTMLElementEventMap, 37 | KM extends keyof MediaQueryListEventMap, 38 | T extends HTMLElement | MediaQueryList | void = void 39 | >( 40 | eventName: KW | KH | KM, 41 | handler: ( 42 | event: 43 | | WindowEventMap[KW] 44 | | HTMLElementEventMap[KH] 45 | | MediaQueryListEventMap[KM] 46 | | Event 47 | ) => void, 48 | element?: RefObject, 49 | options?: boolean | AddEventListenerOptions 50 | ) { 51 | // Create a ref that stores handler 52 | const savedHandler = useRef(handler); 53 | 54 | useEffect(() => { 55 | savedHandler.current = handler; 56 | }, [handler]); 57 | 58 | useEffect(() => { 59 | // Define the listening target 60 | const targetElement: T | Window = element?.current ?? window; 61 | 62 | if (!(targetElement && targetElement.addEventListener)) return; 63 | 64 | // Create event listener that calls handler function stored in ref 65 | const listener: typeof handler = (event) => savedHandler.current(event); 66 | 67 | targetElement.addEventListener(eventName, listener, options); 68 | 69 | // Remove event listener on cleanup 70 | return () => { 71 | targetElement.removeEventListener(eventName, listener, options); 72 | }; 73 | }, [eventName, element, options]); 74 | } 75 | 76 | export { useEventListener }; 77 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": [ 6 | "DOM", 7 | "DOM.Iterable", 8 | "ESNext" 9 | ], 10 | "allowJs": false, 11 | "skipLibCheck": true, 12 | "esModuleInterop": false, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "module": "ESNext", 17 | "moduleResolution": "Node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "noUnusedLocals": false, 22 | "jsx": "react-jsx", 23 | "baseUrl": "./", 24 | "paths": { 25 | "#/*": [ 26 | "src/*" 27 | ], 28 | "#types/*": [ 29 | "src/types/*" 30 | ], 31 | "#assets/*": [ 32 | "src/assets/*" 33 | ], 34 | "#constants/*": [ 35 | "src/constants/*" 36 | ], 37 | "#locales/*": [ 38 | "src/locales/*" 39 | ], 40 | "#models/*": [ 41 | "src/models/*" 42 | ], 43 | "#navigation/*": [ 44 | "src/navigation/*" 45 | ], 46 | "#pages/*": [ 47 | "src/pages/*" 48 | ], 49 | "#service/*": [ 50 | "src/service/*" 51 | ], 52 | "#store/*": [ 53 | "src/store/*" 54 | ], 55 | "#test/*": [ 56 | "src/test/*" 57 | ], 58 | "#utils/*": [ 59 | "src/utils/*" 60 | ], 61 | "#components/*": [ 62 | "src/components/*" 63 | ], 64 | "#hooks/*": [ 65 | "src/hooks/*" 66 | ], 67 | "#routes/*": [ 68 | "src/routes/*" 69 | ], 70 | "#lib/*": [ 71 | "src/lib/*" 72 | ], 73 | "#context/*": [ 74 | "src/context/*" 75 | ], 76 | "#interfaces/*": [ 77 | "src/interfaces/*" 78 | ], 79 | "#mocks/*": [ 80 | "src/mocks/*" 81 | ] 82 | } 83 | }, 84 | "types": [ 85 | "vitest/globals" 86 | ], 87 | "include": [ 88 | "src" 89 | ], 90 | "references": [ 91 | { 92 | "path": "./tsconfig.node.json" 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/image.py: -------------------------------------------------------------------------------- 1 | import math 2 | from typing import * 3 | 4 | import cv2 5 | import numpy as np 6 | 7 | from scipy import ndimage as nd 8 | from skimage import img_as_bool 9 | from skimage.transform import resize 10 | 11 | from .misc import check_tensor 12 | 13 | 14 | def scale_image(image: np.ndarray, mask: Optional[np.ndarray] = None, 15 | resolution: Union[int, Tuple[int, int]] = None, area: int = None, return_mask: bool = False) -> np.ndarray: 16 | 17 | dims = check_tensor(image, "h w c") 18 | check_tensor(mask, "h w", allow_none=True) 19 | 20 | assert 1 <= dims["c"] <= 3, "Invalid number of channels for input image" 21 | assert not (bool(resolution) and bool(area)), "Both scaling values cannot be set!" 22 | 23 | if not resolution and not area: # no scaling parameter set: do nothing 24 | mask = np.ones_like(image[..., 0]).astype("bool") if mask is None else mask 25 | return (image, mask) if return_mask else image 26 | 27 | if resolution: 28 | new_shape = resolution[::-1] if isinstance(resolution, Tuple) else (resolution, resolution) 29 | else: 30 | H, W, C = image.shape 31 | new_W = int(math.sqrt(area / (H / W))) 32 | new_H = int(area / new_W) 33 | new_shape = (new_W, new_H) 34 | 35 | if mask is None: 36 | scaled_image = cv2.resize(image, new_shape, interpolation=cv2.INTER_AREA) 37 | scaled_mask = np.ones_like(scaled_image[..., 0]).astype("bool") 38 | return (scaled_image, scaled_mask) if return_mask else scaled_image 39 | 40 | output_mask = img_as_bool(resize(mask, new_shape, preserve_range=True)) 41 | 42 | indices = nd.distance_transform_edt(~mask.astype('bool'), return_distances=False, return_indices=True) 43 | filled_image = image[tuple(indices)] 44 | scaled_image = cv2.resize(filled_image, new_shape, interpolation=cv2.INTER_AREA) 45 | scaled_image[~output_mask] = 0 46 | 47 | return (scaled_image, output_mask) if return_mask else scaled_image 48 | 49 | 50 | def tight_crop_image(image: np.ndarray, mask: np.ndarray, return_mask: bool = False): 51 | dims = check_tensor(image, "h w c") 52 | check_tensor(mask, "h w") 53 | assert 1 <= dims["c"] <= 3, "Number of dims is invalid!" 54 | 55 | rows = np.any(mask, axis=1) 56 | cols = np.any(mask, axis=0) 57 | row_min, row_max = np.where(rows)[0][[0, -1]] 58 | col_min, col_max = np.where(cols)[0][[0, -1]] 59 | 60 | image_crop = image[row_min:row_max, col_min:col_max] 61 | mask_crop = mask[row_min:row_max, col_min:col_max] 62 | 63 | return (image_crop, mask_crop) if return_mask else image_crop 64 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | /* Normal Fonts */ 7 | @font-face { 8 | font-family: 'Poppins'; 9 | font-style: normal; 10 | font-weight: 400; 11 | font-display: auto; 12 | 13 | src: url(./assets/fonts/Poppins-Regular.ttf); 14 | } 15 | 16 | @font-face { 17 | font-family: 'Poppins'; 18 | font-style: normal; 19 | font-weight: 500; 20 | font-display: auto; 21 | 22 | src: url(./assets/fonts/Poppins-Medium.ttf); 23 | } 24 | 25 | @font-face { 26 | font-family: 'Poppins'; 27 | font-style: normal; 28 | font-weight: 600; 29 | font-display: auto; 30 | 31 | src: url(./assets/fonts/Poppins-SemiBold.ttf); 32 | } 33 | 34 | /* Italic Fonts */ 35 | @font-face { 36 | font-family: 'Poppins'; 37 | font-style: italic; 38 | font-weight: 400; 39 | font-display: auto; 40 | 41 | src: url(./assets/fonts/Poppins-Italic.ttf); 42 | } 43 | 44 | @font-face { 45 | font-family: 'Poppins'; 46 | font-style: italic; 47 | font-weight: 500; 48 | font-display: auto; 49 | 50 | src: url(./assets/fonts/Poppins-MediumItalic.ttf); 51 | } 52 | 53 | @font-face { 54 | font-family: 'Poppins'; 55 | font-style: italic; 56 | font-weight: 600; 57 | font-display: auto; 58 | 59 | src: url(./assets/fonts/Poppins-SemiBoldItalic.ttf); 60 | } 61 | } 62 | 63 | @layer base { 64 | input[type='number']::-webkit-outer-spin-button, 65 | input[type='number']::-webkit-inner-spin-button, 66 | input[type='number'] { 67 | -webkit-appearance: none; 68 | margin: 0; 69 | -moz-appearance: textfield !important; 70 | appearance: none; 71 | } 72 | } 73 | 74 | /* a { 75 | font-weight: 500; 76 | color: #646cff; 77 | text-decoration: inherit; 78 | } 79 | a:hover { 80 | color: #535bf2; 81 | } 82 | 83 | h1 { 84 | font-size: 3.2em; 85 | line-height: 1.1; 86 | } 87 | 88 | button { 89 | border-radius: 8px; 90 | border: 1px solid transparent; 91 | padding: 0.6em 1.2em; 92 | font-size: 1em; 93 | font-weight: 500; 94 | background-color: #1a1a1a; 95 | cursor: pointer; 96 | transition: border-color 0.25s; 97 | } 98 | button:hover { 99 | border-color: #646cff; 100 | } 101 | button:focus, 102 | button:focus-visible { 103 | outline: 4px auto -webkit-focus-ring-color; 104 | } 105 | 106 | @media (prefers-color-scheme: light) { 107 | :root { 108 | color: #213547; 109 | background-color: #ffffff; 110 | } 111 | a:hover { 112 | color: #747bff; 113 | } 114 | button { 115 | background-color: #f9f9f9; 116 | } 117 | } */ 118 | -------------------------------------------------------------------------------- /api/yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /home/paw/.nvm/versions/node/v18.17.1/bin/node /home/paw/.nvm/versions/node/v18.17.1/bin/yarn init 3 | 4 | PATH: 5 | /home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/home/paw/.vscode-server/bin/f1b07bd25dfad64b0167beb15359ae573aecd2cc/bin/remote-cli:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/home/paw/.nvm/versions/node/v18.17.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Python312/Scripts/:/mnt/c/Python312/:/mnt/c/Program Files/ImageMagick-7.1.1-Q16-HDRI/magick.exe:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/ProgramData/chocolatey/bin:/mnt/c/Program Files/Amazon/AWSCLIV2/:/mnt/c/Program Files/Git/cmd:/mnt/c/HashiCorp/Vagrant/bin:/mnt/c/Users/paujo/AppData/Roaming/nvm:/mnt/c/Program Files/nodejs:/mnt/c/Program Files/dotnet/:/mnt/c/Program Files/NVIDIA Corporation/NVIDIA NvDLISR:/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/mnt/c/Users/paujo/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/paujo/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Users/paujo/AppData/Local/gitkraken/bin:/mnt/c/Users/paujo/.dotnet/tools:/mnt/c/Program Files/ImageMagick-7.1.1-Q16-HDRI:/mnt/c/Program Files/Memurai/:/mnt/c/Program Files/Docker/Docker/resources/bin:/mnt/c/Program Files/GitHub CLI/:/mnt/c/Users/paujo/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/paujo/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Users/paujo/AppData/Local/gitkraken/bin:/mnt/c/Users/paujo/AppData/Roaming/nvm:/mnt/c/Program Files/nodejs:/mnt/c/Windows/system32/config/systemprofile/.dotnet/tools:/mnt/c/Users/paujo/AppData/Local/Programs/Lens/resources/cli/bin:/mnt/c/Program Files (x86)/granted:/snap/bin:/home/paw/.dotnet/tools:/home/paw/.pulumi/bin:/home/paw/.local/bin:/usr/share/dotnet:/home/paw/.pulumi/bin:/home/paw/.local/bin:/usr/share/dotnet 6 | 7 | Yarn version: 8 | 1.22.19 9 | 10 | Node version: 11 | 18.17.1 12 | 13 | Platform: 14 | linux x64 15 | 16 | Trace: 17 | Error: canceled 18 | at Interface. (/home/paw/.nvm/versions/node/v18.17.1/lib/node_modules/yarn/lib/cli.js:137150:13) 19 | at Interface.emit (node:events:514:28) 20 | at [_ttyWrite] [as _ttyWrite] (node:internal/readline/interface:1131:18) 21 | at ReadStream.onkeypress (node:internal/readline/interface:270:20) 22 | at ReadStream.emit (node:events:514:28) 23 | at emitKeys (node:internal/readline/utils:357:14) 24 | at emitKeys.next () 25 | at ReadStream.onData (node:internal/readline/emitKeypressEvents:64:36) 26 | at ReadStream.emit (node:events:514:28) 27 | at addChunk (node:internal/streams/readable:324:12) 28 | 29 | npm manifest: 30 | No manifest 31 | 32 | yarn manifest: 33 | No manifest 34 | 35 | Lockfile: 36 | No lockfile 37 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/path.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from typing import * 4 | 5 | 6 | def check_dir(directory: Union[str, Path], exist: bool = True): 7 | directory = Path(directory) 8 | 9 | if exist: 10 | assert directory.is_dir(), f"Directory {directory.resolve()} does not exist!" 11 | else: 12 | assert directory.parent.is_dir(), f"Parent directory {directory.parent.resolve()} does not exist!" 13 | assert not directory.is_dir(), f"Directory {directory.resolve()} does exist!" 14 | 15 | return directory 16 | 17 | 18 | def check_file(file: Union[str, Path], suffix: Union[None, str, List[str]] = None, exist: Optional[bool] = True): 19 | file = Path(file) 20 | 21 | if exist is None: 22 | pass # No check 23 | elif exist: 24 | assert file.is_file(), f"File {file.resolve()} does not exist!" 25 | else: 26 | assert file.parent.is_dir(), f"Parent directory {file.parent.resolve()} does not exist!" 27 | assert not file.is_file(), f"File {file.resolve()} does exist!" 28 | 29 | if suffix is None: 30 | pass 31 | elif isinstance(suffix, str): 32 | assert file.suffix == suffix, f"File {file.resolve()} has an invalid suffix! Allowed is '{suffix}'" 33 | else: 34 | assert file.suffix in suffix, f"File {file.resolve()} has an invalid suffix! Allowed is any of '{suffix}'" 35 | 36 | return file 37 | 38 | 39 | def list_dirs(search_dir: Union[str, Path], recursive: bool = False, as_string: bool = False, glob_string="*"): 40 | search_dir = Path(search_dir) 41 | glob_function = search_dir.rglob if recursive else search_dir.glob 42 | 43 | dirs = [str(file) if as_string else file 44 | for file in glob_function(glob_string) 45 | if file.is_dir()] 46 | return list(sorted(dirs)) 47 | 48 | 49 | def list_files(search_dir: Union[str, Path], suffixes: List[str] = None, recursive: bool = False, 50 | as_string: bool = False): 51 | search_dir = check_dir(search_dir) 52 | 53 | if suffixes is None: 54 | suffixes = [''] 55 | 56 | glob_function = search_dir.rglob if recursive else search_dir.glob 57 | 58 | files = [str(file) if as_string else file 59 | for suffix in suffixes 60 | for file in glob_function("*" + suffix) 61 | if file.is_file()] 62 | 63 | return list(sorted(files)) 64 | 65 | 66 | def is_empty(directory: Union[str, Path]) -> bool: 67 | directory = Path(directory) 68 | return not any(directory.iterdir()) 69 | 70 | 71 | def remove_common_path(path: Union[str, Path], reference: Union[str, Path]) -> Path: 72 | path = Path(path) 73 | reference = Path(reference) 74 | 75 | path = path.expanduser().absolute() 76 | reference = reference.expanduser().absolute() 77 | common_path = os.path.commonpath([str(path), str(reference)]) 78 | return path.relative_to(Path(common_path)) 79 | -------------------------------------------------------------------------------- /Fuentes_de_datos/ApiResultados/api_resultados.py: -------------------------------------------------------------------------------- 1 | from .config import settings 2 | import requests 3 | 4 | 5 | class ApiResultados: 6 | def __init__(self) -> None: 7 | self.url = settings.URL 8 | 9 | 10 | def _get(self,endpoint): 11 | return requests.get(self.url+endpoint).json() 12 | 13 | 14 | def get_resultados_generales(self): 15 | ''' 16 | Obtiene los resultados de las elecciones generales 17 | 18 | Args: None 19 | 20 | Retrun : json 21 | ''' 22 | 23 | endpoint = '/scope/data/getScopeData/00000000000000000000000b/1/1' 24 | return self._get(endpoint) 25 | 26 | 27 | def get_resultados_por_mesa(self, mesa_id): 28 | ''' 29 | Obtiene los resultados segun el numero de mesa 30 | 31 | Args: 32 | mesa_id (str): id de la mesa de la cual se quiere obtener la foto del telgrama. 33 | 34 | Retrun : json 35 | ''' 36 | 37 | endpoint= '/scope/data/getScopeData/' 38 | return self._get(endpoint+mesa_id+'/1') 39 | 40 | 41 | def get_img_telegrama(self,mesa_id:str): 42 | ''' 43 | Obtiene la foto del telegrama en formato tiff 44 | 45 | Args: 46 | mesa_id (str): id de la mesa de la cual se quiere obtener la foto del telgrama. 47 | 48 | Returns: 49 | json: 50 | "encodingBinary": Binario de la imagen en base64 51 | "fileName": Nombre definido para la imagen, 52 | "imagenState": { 53 | "state": NN, 54 | "date": NN, 55 | }, 56 | "bloqueoState": { 57 | "state": NN, 58 | "date": NN 59 | }, 60 | "incidenciaState": NN, 61 | "metadatos": { 62 | "hash": NN, 63 | "pollingStationCode": "id de la mesa", 64 | "pages": [ 65 | { 66 | "status": NN, 67 | "scanningDate": NN, 68 | "scanningUser": DNI EMISOR, 69 | "pageNumber": NN, 70 | "transmissionDate": Fecha emision, 71 | "transmissionUserId": DNI EMISOR, 72 | "transmissionUserName": NOMBRE EMISOR 73 | } 74 | ] 75 | }, 76 | "hash": "NN", 77 | "scopeId": NN 78 | } 79 | 80 | Nota : Los valores que aparece como NN son valores que no pude 81 | interpretar a que hacen referencia 82 | 83 | ''' 84 | 85 | endpoint = '/scope/data/getTiff/' 86 | return self._get(endpoint+mesa_id) 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/load.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" 4 | 5 | import json 6 | from pathlib import Path 7 | from typing import * 8 | 9 | import cv2 10 | import numpy as np 11 | import yaml 12 | import h5py 13 | 14 | from .path import check_file 15 | 16 | 17 | def load_image(file: Path): 18 | file = check_file(file, suffix=[".png", ".jpg"]) 19 | return cv2.imread(str(file), cv2.IMREAD_COLOR) 20 | 21 | 22 | def save_image(file: Path, data: np.ndarray, override: bool = False): 23 | exist = None if override else False 24 | file = check_file(file, suffix=[".png", ".jpg"], exist=exist) 25 | assert data.dtype == np.uint8 26 | assert len(data.shape) in [2, 3] 27 | if len(data.shape) == 3: 28 | assert data.shape[2] == 3 29 | 30 | cv2.imwrite(str(file), data) 31 | 32 | 33 | def load_npz(file: Path) -> np.ndarray: 34 | file = check_file(file, suffix=".npz") 35 | with np.load(file) as archive: 36 | keys = list(archive.keys()) 37 | assert len(keys) == 1 38 | return archive[keys[0]] 39 | 40 | 41 | def load_exr(file: Path): 42 | file = check_file(file, suffix=".exr") 43 | return cv2.imread(str(file.resolve()), cv2.IMREAD_UNCHANGED) 44 | 45 | 46 | def load_mat(file: Path, key: str = None): 47 | file = check_file(file, suffix=".mat") 48 | 49 | with h5py.File(str(file.resolve()), 'r') as f: 50 | if key is None: 51 | key = list(f.keys())[0] 52 | return np.array(f[key]) 53 | 54 | 55 | def save_npz(file: Path, data: np.ndarray, override: bool = False): 56 | exist = None if override else False 57 | file = check_file(file, suffix=".npz", exist=exist) 58 | 59 | params = { 60 | file.stem: data 61 | } 62 | np.savez_compressed(str(file), **params) 63 | 64 | 65 | def load_array(file: Path): 66 | file = Path(file) 67 | 68 | if file.suffix == ".npz": 69 | return load_npz(file) 70 | 71 | if file.suffix == ".exr": 72 | return load_exr(file) 73 | 74 | if file.suffix == ".mat": 75 | return load_mat(file) 76 | 77 | assert False, f"Cannot load array! Unknown file extension {file.suffix}!" 78 | 79 | 80 | def load_json(file: Union[str, Path]): 81 | file = check_file(file, suffix=".json", exist=True) 82 | with file.open("r") as fp: 83 | return json.load(fp) 84 | 85 | 86 | def save_json(file: Path, data: Dict, exist=False): 87 | file = check_file(file, suffix=".json", exist=exist) 88 | with file.open("w") as f: 89 | json.dump(obj=data, fp=f, indent=4) 90 | 91 | 92 | def load_yaml(file: Union[str, Path]): 93 | file = check_file(file, suffix=".yaml", exist=True) 94 | with file.open("r") as fp: 95 | return yaml.safe_load(fp) 96 | 97 | 98 | def save_yaml(file: Path, data: Dict, exist=False): 99 | file = check_file(file, suffix=".yaml", exist=exist) 100 | with file.open("w") as f: 101 | yaml.dump(data, f, default_flow_style=False) 102 | -------------------------------------------------------------------------------- /api/swagger-output.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "LLA Fraud detection API", 5 | "description": "API for the LLA Fraud detection project", 6 | "version": "1.0.0" 7 | }, 8 | "host": "localhost:3000", 9 | "basePath": "/api", 10 | "schemes": [ 11 | "http" 12 | ], 13 | "paths": { 14 | "/denuncia": { 15 | "get": { 16 | "description": "", 17 | "responses": { 18 | "200": { 19 | "description": "OK" 20 | } 21 | } 22 | } 23 | }, 24 | "/denuncia/{id}": { 25 | "get": { 26 | "description": "", 27 | "parameters": [ 28 | { 29 | "name": "id", 30 | "in": "path", 31 | "required": true, 32 | "type": "string" 33 | } 34 | ], 35 | "responses": { 36 | "200": { 37 | "description": "OK" 38 | } 39 | } 40 | } 41 | }, 42 | "/fiscalizar": { 43 | "post": { 44 | "description": "", 45 | "responses": { 46 | "201": { 47 | "description": "Created" 48 | } 49 | } 50 | } 51 | }, 52 | "/mesa/{id}": { 53 | "get": { 54 | "description": "", 55 | "parameters": [ 56 | { 57 | "name": "id", 58 | "in": "path", 59 | "required": true, 60 | "type": "string" 61 | } 62 | ], 63 | "responses": { 64 | "200": { 65 | "description": "OK" 66 | } 67 | } 68 | } 69 | }, 70 | "/mesa": { 71 | "get": { 72 | "description": "", 73 | "responses": { 74 | "200": { 75 | "description": "OK" 76 | } 77 | } 78 | } 79 | }, 80 | "/user": { 81 | "post": { 82 | "description": "", 83 | "responses": { 84 | "201": { 85 | "description": "Created" 86 | } 87 | } 88 | }, 89 | "get": { 90 | "description": "", 91 | "responses": { 92 | "200": { 93 | "description": "OK" 94 | } 95 | } 96 | } 97 | }, 98 | "/user/{id}": { 99 | "get": { 100 | "description": "", 101 | "parameters": [ 102 | { 103 | "name": "id", 104 | "in": "path", 105 | "required": true, 106 | "type": "string" 107 | } 108 | ], 109 | "responses": { 110 | "200": { 111 | "description": "OK" 112 | } 113 | } 114 | } 115 | }, 116 | "/user/{id}/roles": { 117 | "get": { 118 | "description": "", 119 | "parameters": [ 120 | { 121 | "name": "id", 122 | "in": "path", 123 | "required": true, 124 | "type": "string" 125 | } 126 | ], 127 | "responses": { 128 | "200": { 129 | "description": "OK" 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_util/misc.py: -------------------------------------------------------------------------------- 1 | from .load import load_image, load_npz 2 | import re 3 | from pathlib import Path 4 | from typing import * 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | import torch 9 | import torch.nn.functional as F 10 | from einops import rearrange 11 | 12 | plt.rcParams['figure.figsize'] = [10, 10] 13 | 14 | 15 | def check_tensor(data, pattern: str, allow_none: bool = False, **kwargs): 16 | if allow_none and data is None: 17 | return {} 18 | 19 | assert bool(re.match('^[a-zA-Z0-9 ]+$', pattern)), "Invalid characters in pattern found! Only use [a-zA-Z0-9 ]." 20 | 21 | tokens = [t for t in pattern.split(" ") if t != ''] 22 | 23 | assert len(data.shape) == len(tokens), "Input tensor has an invalid number of dimensions!" 24 | 25 | assignment = {} 26 | for dim, (token, size) in enumerate(zip(tokens, data.shape)): 27 | if token[0].isdigit(): 28 | assert int(token) == size, f"Tensor mismatch in dimension {dim}: expected {size}, found {int(token)}!" 29 | else: 30 | if token in assignment: 31 | assert assignment[ 32 | token] == size, f"Tensor mismatch in dimension {dim}: expected {size}, found {assignment[token]}!" 33 | else: 34 | assignment[token] = size 35 | 36 | if token in kwargs: 37 | assert kwargs[ 38 | token] == size, f"Tensor mismatch in dimension {dim}: expected {kwargs[token]}, found {size}!" 39 | 40 | return assignment 41 | 42 | 43 | def to_numpy_image(image: Union[Path, np.ndarray, torch.Tensor]): 44 | if isinstance(image, Path): 45 | image = load_image(image) 46 | elif isinstance(image, torch.Tensor): 47 | image = image.detach().cpu().numpy() 48 | 49 | if np.issubdtype(image.dtype, np.floating): 50 | image = (image * 255).astype("uint8") 51 | 52 | image = np.squeeze(image) 53 | 54 | if image.shape[0] <= 3: 55 | image = rearrange(image, "c h w -> h w c") 56 | 57 | dims = check_tensor(image, "h w c") 58 | assert dims["c"] <= 3 59 | 60 | padding = np.zeros((dims["h"], dims["w"], 3 - dims["c"]), dtype=np.uint8) 61 | return np.concatenate([image, padding], axis=-1) 62 | 63 | 64 | def to_numpy_map(input_map: Union[Path, np.ndarray, torch.Tensor]): 65 | if isinstance(input_map, Path): 66 | input_map = load_npz(input_map) 67 | elif isinstance(input_map, torch.Tensor): 68 | input_map = input_map.detach().cpu().numpy() 69 | 70 | input_map = np.squeeze(input_map) 71 | 72 | if input_map.shape[0] == 2: 73 | input_map = rearrange(input_map, "c h w -> h w c") 74 | 75 | check_tensor(input_map, "h w 2") 76 | return input_map 77 | 78 | 79 | def median_blur(x: torch.Tensor): 80 | dims = check_tensor(x, "n c h w") 81 | assert dims["c"] in [2, 3] 82 | 83 | kernel = torch.ones((1, 1, 3, 3)) / 9 84 | if x.is_cuda: 85 | kernel = kernel.to(x.get_device()) 86 | 87 | x = rearrange(x, "n c h w -> (n c) 1 h w") 88 | x = torch.nn.ReplicationPad2d(1)(x) 89 | x = F.conv2d(x, kernel) 90 | x = rearrange(x, "(n c) 1 h w -> n c h w", c=dims["c"]) 91 | 92 | return x -------------------------------------------------------------------------------- /api/swagger.yaml: -------------------------------------------------------------------------------- 1 | openapi: '3.0.0' 2 | info: 3 | title: 'Election Fraud Prevention API' 4 | version: '1.0.0' 5 | 6 | paths: 7 | # User Features 8 | /api/users: 9 | post: 10 | summary: Create a new user by the party (LLA) 11 | security: 12 | - BearerAuth: [] 13 | requestBody: 14 | content: 15 | application/json: 16 | schema: 17 | $ref: '#/components/schemas/User' 18 | responses: 19 | '201': 20 | description: User created 21 | 22 | # User Roles and Identity 23 | /api/users/{id}/roles: 24 | get: 25 | summary: Get user roles 26 | security: 27 | - BearerAuth: [] 28 | responses: 29 | '200': 30 | description: Roles returned 31 | 32 | # Muestra Resultados Público 33 | /api/voting-tables/{id}: 34 | get: 35 | summary: Get Mesa Data 36 | responses: 37 | '200': 38 | description: Mesa data returned 39 | 40 | /api/voting-tables: 41 | get: 42 | summary: Search Mesas 43 | responses: 44 | '200': 45 | description: Mesas list 46 | 47 | # Denuncias 48 | /api/reports: 49 | get: 50 | summary: List or Search Denuncias 51 | responses: 52 | '200': 53 | description: Denuncias list 54 | 55 | /api/reports/{id}: 56 | get: 57 | summary: Get Specific Denuncia 58 | responses: 59 | '200': 60 | description: Denuncia data 61 | 62 | # Fiscal General to Fiscal Mesa 63 | /api/audit: 64 | post: 65 | summary: Fiscal general evaluates fiscal mesa 66 | security: 67 | - BearerAuth: [] 68 | requestBody: 69 | content: 70 | application/json: 71 | schema: 72 | $ref: '#/components/schemas/Fiscalizar' 73 | responses: 74 | '201': 75 | description: Fiscalization recorded 76 | 77 | # Upload File 78 | /api/upload: 79 | post: 80 | summary: Upload a file to the server 81 | requestBody: 82 | content: 83 | multipart/form-data: 84 | schema: 85 | type: object 86 | properties: 87 | file: 88 | type: string 89 | format: binary 90 | responses: 91 | '200': 92 | description: File uploaded successfully 93 | '500': 94 | description: Internal server error 95 | 96 | components: 97 | schemas: 98 | User: 99 | type: object 100 | properties: 101 | id: 102 | type: string 103 | roles: 104 | type: array 105 | items: 106 | type: string 107 | whatsapp: 108 | type: string 109 | mesa_id: 110 | type: string 111 | Fiscalizar: 112 | type: object 113 | properties: 114 | imagen: 115 | type: string 116 | validado: 117 | type: boolean 118 | errores: 119 | type: boolean 120 | observaciones: 121 | type: string 122 | 123 | security: 124 | BearerAuth: 125 | type: http 126 | scheme: bearer 127 | -------------------------------------------------------------------------------- /frontend/src/pages/results/filter.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { observer } from 'mobx-react-lite'; 3 | import { Selector } from '#/components/selector'; 4 | import Button from '#/components/button'; 5 | import Navbar from '#/components/navbar'; 6 | 7 | const dummyData = [ 8 | { key: 'ex1', label: 'Example' }, 9 | { key: 'ex2', label: 'Example 2' }, 10 | { key: 'ex3', label: 'Example 3' }, 11 | ]; 12 | 13 | const FilterPage = () => { 14 | const [distrito, setDistrito] = useState(''); 15 | const [seccionElectoral, setSeccionElectoral] = useState(''); 16 | const [seccion, setSeccion] = useState(''); 17 | const [municipio, setMunicipio] = useState(''); 18 | const [circuito, setCircuito] = useState(''); 19 | const [establecimiento, setEstablecimiento] = useState(''); 20 | const [mesa, setMesa] = useState(''); 21 | 22 | return ( 23 | <> 24 | 25 |
26 |
27 |

Resultados totales

28 |
29 | 35 | 41 | 47 | 53 | 59 | 65 | 71 |
72 | 73 |
84 |
85 | 86 | ); 87 | }; 88 | 89 | export const Filter = observer(FilterPage); 90 | 91 | export default Filter; 92 | -------------------------------------------------------------------------------- /frontend/src/utils/http.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; 2 | 3 | import { sleep } from './utils'; 4 | import { eventBus } from './event-bus'; 5 | 6 | type Data = Record; 7 | 8 | export class Http { 9 | public isInternetReachable = true; 10 | private waitForConnectionPromise: Promise | undefined; 11 | 12 | public constructor() { 13 | axios.defaults.baseURL = '/api'; 14 | } 15 | 16 | public setConnection(isInternetReachable: boolean) { 17 | this.isInternetReachable = isInternetReachable; 18 | } 19 | 20 | public async waitForConnection(maxRetries = 10) { 21 | if (this.isInternetReachable) { 22 | return; 23 | } 24 | 25 | if (this.waitForConnectionPromise) { 26 | return this.waitForConnectionPromise; 27 | } 28 | 29 | for (let index = 0; index <= maxRetries; index++) { 30 | this.waitForConnectionPromise = sleep(1000); 31 | 32 | await this.waitForConnectionPromise; 33 | 34 | this.waitForConnectionPromise = undefined; 35 | 36 | if (this.isInternetReachable) { 37 | return; 38 | } 39 | } 40 | } 41 | 42 | public async get(path: string, options: AxiosRequestConfig = {}) { 43 | await this.waitForConnection().catch(); 44 | 45 | return axios.get(path, options).then(this.mapData).catch(this.handleError); 46 | } 47 | 48 | public async post( 49 | path: string, 50 | data?: Data, 51 | options: AxiosRequestConfig = {} 52 | ) { 53 | await this.waitForConnection().catch(); 54 | 55 | return axios 56 | .post(path, data, options) 57 | .then(this.mapData) 58 | .catch(this.handleError); 59 | } 60 | 61 | public async put( 62 | path: string, 63 | data?: Data, 64 | options: AxiosRequestConfig = {} 65 | ) { 66 | await this.waitForConnection().catch(); 67 | 68 | return axios 69 | .put(path, data, options) 70 | .then(this.mapData) 71 | .catch(this.handleError); 72 | } 73 | 74 | public async patch( 75 | path: string, 76 | data?: Data, 77 | options: AxiosRequestConfig = {} 78 | ) { 79 | await this.waitForConnection().catch(); 80 | 81 | return axios 82 | .patch(path, data, options) 83 | .then(this.mapData) 84 | .catch(this.handleError); 85 | } 86 | 87 | public async delete(path: string, options: AxiosRequestConfig = {}) { 88 | await this.waitForConnection().catch(); 89 | 90 | return axios 91 | .delete(path, options) 92 | .then(this.mapData) 93 | .catch(this.handleError); 94 | } 95 | 96 | private mapData = (result: AxiosResponse) => { 97 | const error = result?.data?.errors?.[0]?.extensions?.code; 98 | 99 | const hasErrors = result?.data?.errors?.length > 0; 100 | 101 | if (hasErrors) { 102 | if (error === 'UNAUTHENTICATED') { 103 | eventBus.emit('UNAUTHORIZED'); 104 | } else { 105 | this.handleError(result?.data?.errors[0]); 106 | } 107 | } 108 | return result.data; 109 | }; 110 | 111 | private handleError = (error: AxiosError) => { 112 | if (error.response?.status === 401) { 113 | eventBus.emit('UNAUTHORIZED'); 114 | } 115 | 116 | throw error; 117 | }; 118 | } 119 | 120 | export const http = new Http(); 121 | -------------------------------------------------------------------------------- /frontend/src/assets/logos/fenix-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/components/input/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import classNames from 'classnames'; 3 | import { IInputProps } from './types'; 4 | 5 | const Input: React.FC = ({ 6 | label, 7 | appearance, 8 | type, 9 | id, 10 | placeholder, 11 | error, 12 | 13 | className, 14 | labelClassName, 15 | inputClassName, 16 | 17 | onChange, 18 | onBlur, 19 | }) => { 20 | const [isPasswordVisible, setIsPasswordVisible] = useState(false); 21 | 22 | const outlineLabelApperence: string = 23 | 'border-2 relative rounded-xl bg-white shadow-md border-gray-300 focus-within:border-violet-light'; 24 | const underlineLabelApperence: string = 25 | 'border-b-2 border-gray-300 focus-within:border-violet-light text-center'; 26 | const labelApperence: string = 27 | appearance === 'underline' 28 | ? underlineLabelApperence 29 | : outlineLabelApperence; 30 | 31 | const outlineSpanApperence: string = 32 | 'absolute top-1/3 transition-all transform duration-300 ease-in-out group-focus-within:-translate-y-full group-focus-within:text-sm -translate-y-1/2 pointer-events-none'; 33 | const spanApperence: string = 34 | appearance === 'underline' ? '' : outlineSpanApperence; 35 | 36 | const outlineInputApperence: string = 'pt-6 pb-1'; 37 | const underlineInputApperence: string = 'text-center'; 38 | const inputApperence: string = 39 | appearance === 'underline' 40 | ? underlineInputApperence 41 | : outlineInputApperence; 42 | 43 | return ( 44 |
45 |
53 | 64 |
65 | 78 | {type === 'password' && 79 | (isPasswordVisible ? ( 80 | Show password setIsPasswordVisible(false)} 85 | /> 86 | ) : ( 87 | Hide password setIsPasswordVisible(true)} 92 | /> 93 | ))} 94 |
95 |
96 |
97 | ); 98 | }; 99 | 100 | export default Input; 101 | -------------------------------------------------------------------------------- /frontend/src/assets/icon/mail-person.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/navbar/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const Navbar: React.FC = () => { 5 | const [menuOpen, setMenuOpen] = useState(false); 6 | 7 | return ( 8 |
9 |
10 |
11 | Logo 16 |
17 |
18 |
setMenuOpen(!menuOpen)} 21 | > 22 | {!menuOpen ? ( 23 | User profile 28 | ) : ( 29 | User profile 34 | )} 35 |
36 | {menuOpen && ( 37 |
38 |
39 | 40 | 41 | 42 |
43 |
44 | Javier 45 |
46 |
47 | {/* El gris pactado no se parece al de figma */} 48 | 52 | Mi cuenta 53 | 54 | 58 | Cargar resultados de mesa 59 | 60 | alert('No existe la ruta aún')} 64 | > 65 | Impugnar mesa 66 | 67 | alert('No existe la ruta aún')} 71 | > 72 | Denunciar Irregularidades 73 | 74 | 78 | Ver resultados 79 | 80 |
81 |
82 |
83 | User profile 88 | Cerrar sesión 89 |
90 |
91 |
92 | )} 93 |
94 |
95 |
96 | ); 97 | }; 98 | 99 | export default Navbar; 100 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/extractor.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class ResidualBlock(nn.Module): 5 | def __init__(self, in_planes, planes, norm_fn='group', stride=1): 6 | super(ResidualBlock, self).__init__() 7 | 8 | self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, padding=1, stride=stride) 9 | self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, padding=1) 10 | self.relu = nn.ReLU(inplace=True) 11 | 12 | num_groups = planes // 8 13 | 14 | if norm_fn == 'group': 15 | self.norm1 = nn.GroupNorm(num_groups=num_groups, num_channels=planes) 16 | self.norm2 = nn.GroupNorm(num_groups=num_groups, num_channels=planes) 17 | if not stride == 1: 18 | self.norm3 = nn.GroupNorm(num_groups=num_groups, num_channels=planes) 19 | 20 | elif norm_fn == 'batch': 21 | self.norm1 = nn.BatchNorm2d(planes) 22 | self.norm2 = nn.BatchNorm2d(planes) 23 | if not stride == 1: 24 | self.norm3 = nn.BatchNorm2d(planes) 25 | 26 | elif norm_fn == 'instance': 27 | self.norm1 = nn.InstanceNorm2d(planes) 28 | self.norm2 = nn.InstanceNorm2d(planes) 29 | if not stride == 1: 30 | self.norm3 = nn.InstanceNorm2d(planes) 31 | 32 | elif norm_fn == 'none': 33 | self.norm1 = nn.Sequential() 34 | self.norm2 = nn.Sequential() 35 | if not stride == 1: 36 | self.norm3 = nn.Sequential() 37 | 38 | if stride == 1: 39 | self.downsample = None 40 | 41 | else: 42 | self.downsample = nn.Sequential( 43 | nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride), self.norm3) 44 | 45 | def forward(self, x): 46 | y = x 47 | y = self.relu(self.norm1(self.conv1(y))) 48 | y = self.relu(self.norm2(self.conv2(y))) 49 | 50 | if self.downsample is not None: 51 | x = self.downsample(x) 52 | 53 | return self.relu(x + y) 54 | 55 | 56 | class BasicEncoder(nn.Module): 57 | def __init__(self, output_dim=128, norm_fn='batch'): 58 | super(BasicEncoder, self).__init__() 59 | self.norm_fn = norm_fn 60 | 61 | if self.norm_fn == 'group': 62 | self.norm1 = nn.GroupNorm(num_groups=8, num_channels=64) 63 | 64 | elif self.norm_fn == 'batch': 65 | self.norm1 = nn.BatchNorm2d(64) 66 | 67 | elif self.norm_fn == 'instance': 68 | self.norm1 = nn.InstanceNorm2d(64) 69 | 70 | elif self.norm_fn == 'none': 71 | self.norm1 = nn.Sequential() 72 | 73 | self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3) 74 | self.relu1 = nn.ReLU(inplace=True) 75 | 76 | self.in_planes = 64 77 | self.layer1 = self._make_layer(64, stride=1) 78 | self.layer2 = self._make_layer(128, stride=2) 79 | self.layer3 = self._make_layer(192, stride=2) 80 | 81 | # output convolution 82 | self.conv2 = nn.Conv2d(192, output_dim, kernel_size=1) 83 | 84 | for m in self.modules(): 85 | if isinstance(m, nn.Conv2d): 86 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 87 | elif isinstance(m, (nn.BatchNorm2d, nn.InstanceNorm2d, nn.GroupNorm)): 88 | if m.weight is not None: 89 | nn.init.constant_(m.weight, 1) 90 | if m.bias is not None: 91 | nn.init.constant_(m.bias, 0) 92 | 93 | def _make_layer(self, dim, stride=1): 94 | layer1 = ResidualBlock(self.in_planes, dim, self.norm_fn, stride=stride) 95 | layer2 = ResidualBlock(dim, dim, self.norm_fn, stride=1) 96 | layers = (layer1, layer2) 97 | 98 | self.in_planes = dim 99 | return nn.Sequential(*layers) 100 | 101 | def forward(self, x): 102 | x = self.conv1(x) 103 | x = self.norm1(x) 104 | x = self.relu1(x) 105 | 106 | x = self.layer1(x) 107 | x = self.layer2(x) 108 | x = self.layer3(x) 109 | 110 | x = self.conv2(x) 111 | 112 | return x 113 | -------------------------------------------------------------------------------- /Fuentes_de_datos/alineamiento_de_imagenes/src/inv3d_model/models/geotr/position_encoding.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | """ 3 | Various positional encodings for the transformer. 4 | """ 5 | import math 6 | import os 7 | from typing import Optional 8 | 9 | import torch 10 | from torch import Tensor 11 | from torch import nn 12 | 13 | 14 | class NestedTensor(object): 15 | def __init__(self, tensors, mask: Optional[Tensor]): 16 | self.tensors = tensors 17 | self.mask = mask 18 | 19 | def to(self, device): 20 | # type: (Device) -> NestedTensor # noqa 21 | cast_tensor = self.tensors.to(device) 22 | mask = self.mask 23 | if mask is not None: 24 | assert mask is not None 25 | cast_mask = mask.to(device) 26 | else: 27 | cast_mask = None 28 | return NestedTensor(cast_tensor, cast_mask) 29 | 30 | def decompose(self): 31 | return self.tensors, self.mask 32 | 33 | def __repr__(self): 34 | return str(self.tensors) 35 | 36 | 37 | class PositionEmbeddingSine(nn.Module): 38 | """ 39 | This is a more standard version of the position embedding, very similar to the one 40 | used by the Attention is all you need paper, generalized to work on images. 41 | """ 42 | 43 | def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None): 44 | super().__init__() 45 | self.num_pos_feats = num_pos_feats 46 | self.temperature = temperature 47 | self.normalize = normalize 48 | if scale is not None and normalize is False: 49 | raise ValueError("normalize should be True if scale is passed") 50 | if scale is None: 51 | scale = 2 * math.pi 52 | self.scale = scale 53 | 54 | def forward(self, mask): 55 | assert mask is not None 56 | y_embed = mask.cumsum(1, dtype=torch.float32) 57 | x_embed = mask.cumsum(2, dtype=torch.float32) 58 | if self.normalize: 59 | eps = 1e-6 60 | y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale 61 | x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale 62 | 63 | dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32).cuda() 64 | dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats) 65 | 66 | pos_x = x_embed[:, :, :, None] / dim_t 67 | pos_y = y_embed[:, :, :, None] / dim_t 68 | pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3) 69 | pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3) 70 | pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) 71 | # print(pos.shape) 72 | return pos 73 | 74 | 75 | class PositionEmbeddingLearned(nn.Module): 76 | """ 77 | Absolute pos embedding, learned. 78 | """ 79 | 80 | def __init__(self, num_pos_feats=256): 81 | super().__init__() 82 | self.row_embed = nn.Embedding(50, num_pos_feats) 83 | self.col_embed = nn.Embedding(50, num_pos_feats) 84 | self.reset_parameters() 85 | 86 | def reset_parameters(self): 87 | nn.init.uniform_(self.row_embed.weight) 88 | nn.init.uniform_(self.col_embed.weight) 89 | 90 | def forward(self, tensor_list: NestedTensor): 91 | x = tensor_list.tensors 92 | h, w = x.shape[-2:] 93 | i = torch.arange(w, device=x.device) 94 | j = torch.arange(h, device=x.device) 95 | x_emb = self.col_embed(i) 96 | y_emb = self.row_embed(j) 97 | pos = torch.cat([ 98 | x_emb.unsqueeze(0).repeat(h, 1, 1), 99 | y_emb.unsqueeze(1).repeat(1, w, 1), 100 | ], dim=-1).permute(2, 0, 1).unsqueeze(0).repeat(x.shape[0], 1, 1, 1) 101 | return pos 102 | 103 | 104 | def build_position_encoding(hidden_dim=512, position_embedding='sine'): 105 | N_steps = hidden_dim // 2 106 | if position_embedding in ('v2', 'sine'): 107 | position_embedding = PositionEmbeddingSine(N_steps, normalize=True) 108 | elif position_embedding in ('v3', 'learned'): 109 | position_embedding = PositionEmbeddingLearned(N_steps) 110 | else: 111 | raise ValueError(f"not supported {position_embedding}") 112 | 113 | return position_embedding 114 | --------------------------------------------------------------------------------