Test Docker

This commit is contained in:
2025-08-15 17:31:51 -03:00
parent 39b1e97072
commit bce5b1dcec
97 changed files with 8493 additions and 216 deletions

View File

@@ -9,11 +9,14 @@
"version": "0.0.0",
"dependencies": {
"axios": "^1.11.0",
"d3": "^7.9.0",
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
"@types/d3": "^7.4.3",
"@types/geojson": "^7946.0.16",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
@@ -1393,6 +1396,290 @@
"@babel/types": "^7.28.2"
}
},
"node_modules/@types/d3": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-array": "*",
"@types/d3-axis": "*",
"@types/d3-brush": "*",
"@types/d3-chord": "*",
"@types/d3-color": "*",
"@types/d3-contour": "*",
"@types/d3-delaunay": "*",
"@types/d3-dispatch": "*",
"@types/d3-drag": "*",
"@types/d3-dsv": "*",
"@types/d3-ease": "*",
"@types/d3-fetch": "*",
"@types/d3-force": "*",
"@types/d3-format": "*",
"@types/d3-geo": "*",
"@types/d3-hierarchy": "*",
"@types/d3-interpolate": "*",
"@types/d3-path": "*",
"@types/d3-polygon": "*",
"@types/d3-quadtree": "*",
"@types/d3-random": "*",
"@types/d3-scale": "*",
"@types/d3-scale-chromatic": "*",
"@types/d3-selection": "*",
"@types/d3-shape": "*",
"@types/d3-time": "*",
"@types/d3-time-format": "*",
"@types/d3-timer": "*",
"@types/d3-transition": "*",
"@types/d3-zoom": "*"
}
},
"node_modules/@types/d3-array": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-axis": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-brush": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-chord": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-contour": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-array": "*",
"@types/geojson": "*"
}
},
"node_modules/@types/d3-delaunay": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-dispatch": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
"integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-drag": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-dsv": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-fetch": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-dsv": "*"
}
},
"node_modules/@types/d3-force": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
"integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-format": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-geo": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/d3-hierarchy": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
"integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-polygon": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-quadtree": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-random": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-scale-chromatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
"integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-selection": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-shape": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-time-format": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/d3-transition": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-selection": "*"
}
},
"node_modules/@types/d3-zoom": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/d3-interpolate": "*",
"@types/d3-selection": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -1400,6 +1687,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1956,6 +2250,15 @@
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1992,6 +2295,407 @@
"dev": true,
"license": "MIT"
},
"node_modules/d3": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
"license": "ISC",
"dependencies": {
"d3-array": "3",
"d3-axis": "3",
"d3-brush": "3",
"d3-chord": "3",
"d3-color": "3",
"d3-contour": "4",
"d3-delaunay": "6",
"d3-dispatch": "3",
"d3-drag": "3",
"d3-dsv": "3",
"d3-ease": "3",
"d3-fetch": "3",
"d3-force": "3",
"d3-format": "3",
"d3-geo": "3",
"d3-hierarchy": "3",
"d3-interpolate": "3",
"d3-path": "3",
"d3-polygon": "3",
"d3-quadtree": "3",
"d3-random": "3",
"d3-scale": "4",
"d3-scale-chromatic": "3",
"d3-selection": "3",
"d3-shape": "3",
"d3-time": "3",
"d3-time-format": "4",
"d3-timer": "3",
"d3-transition": "3",
"d3-zoom": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-axis": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-brush": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
"license": "ISC",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-drag": "2 - 3",
"d3-interpolate": "1 - 3",
"d3-selection": "3",
"d3-transition": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-chord": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
"license": "ISC",
"dependencies": {
"d3-path": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-contour": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
"license": "ISC",
"dependencies": {
"d3-array": "^3.2.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-delaunay": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
"license": "ISC",
"dependencies": {
"delaunator": "5"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dispatch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-drag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
"license": "ISC",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-selection": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dsv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
"license": "ISC",
"dependencies": {
"commander": "7",
"iconv-lite": "0.6",
"rw": "1"
},
"bin": {
"csv2json": "bin/dsv2json.js",
"csv2tsv": "bin/dsv2dsv.js",
"dsv2dsv": "bin/dsv2dsv.js",
"dsv2json": "bin/dsv2json.js",
"json2csv": "bin/json2dsv.js",
"json2dsv": "bin/json2dsv.js",
"json2tsv": "bin/json2dsv.js",
"tsv2csv": "bin/dsv2dsv.js",
"tsv2json": "bin/dsv2json.js"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-fetch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
"license": "ISC",
"dependencies": {
"d3-dsv": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-force": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
"license": "ISC",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-quadtree": "1 - 3",
"d3-timer": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-geo": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2.5.0 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-hierarchy": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-polygon": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-quadtree": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-random": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale-chromatic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3",
"d3-interpolate": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-selection": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-transition": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3",
"d3-dispatch": "1 - 3",
"d3-ease": "1 - 3",
"d3-interpolate": "1 - 3",
"d3-timer": "1 - 3"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"d3-selection": "2 - 3"
}
},
"node_modules/d3-zoom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
"license": "ISC",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-drag": "2 - 3",
"d3-interpolate": "1 - 3",
"d3-selection": "2 - 3",
"d3-transition": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -2017,6 +2721,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
"license": "ISC",
"dependencies": {
"robust-predicates": "^3.0.2"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -2661,6 +3374,18 @@
"node": ">= 0.4"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2698,6 +3423,15 @@
"node": ">=0.8.19"
}
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3200,6 +3934,12 @@
"node": ">=0.10.0"
}
},
"node_modules/robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
"license": "Unlicense"
},
"node_modules/rollup": {
"version": "4.46.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
@@ -3264,6 +4004,18 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
"license": "BSD-3-Clause"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/scheduler": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",

View File

@@ -11,11 +11,14 @@
},
"dependencies": {
"axios": "^1.11.0",
"d3": "^7.9.0",
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
"@types/d3": "^7.4.3",
"@types/geojson": "^7946.0.16",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,145 @@
{
"100": "100",
"101": "101",
"102": "102",
"103": "103",
"104": "104",
"105": "105",
"106": "106",
"107": "107",
"108": "108",
"109": "109",
"110": "110",
"111": "111",
"113": "113",
"114": "114",
"115": "115",
"116": "116",
"117": "117",
"118": "118",
"119": "119",
"120": "120",
"121": "121",
"122": "122",
"123": "123",
"124": "124",
"125": "125",
"126": "126",
"127": "127",
"128": "128",
"129": "129",
"130": "130",
"131": "131",
"132": "132",
"133": "133",
"134": "134",
"135": "135",
"136": "136",
"137": "137",
"309": "309",
"314": "314",
"338": "338",
"357": "357",
"387": "387",
"396": "396",
"398": "398",
"399": "399",
"045": "045",
"055": "055",
"070": "070",
"030": "030",
"074": "074",
"003": "003",
"086": "086",
"082": "082",
"063": "063",
"006": "006",
"047": "047",
"084": "084",
"026": "026",
"025": "025",
"007": "007",
"033": "033",
"076": "076",
"054": "054",
"072": "072",
"012": "012",
"004": "004",
"077": "077",
"087": "087",
"098": "098",
"015": "015",
"080": "080",
"046": "046",
"038": "038",
"032": "032",
"014": "014",
"064": "064",
"083": "083",
"049": "049",
"058": "058",
"023": "023",
"060": "060",
"018": "018",
"042": "042",
"062": "062",
"066": "066",
"041": "041",
"053": "053",
"057": "057",
"001": "001",
"005": "005",
"002": "002",
"031": "031",
"008": "008",
"009": "009",
"013": "013",
"010": "010",
"011": "011",
"019": "019",
"017": "017",
"097": "097",
"020": "020",
"016": "016",
"037": "037",
"021": "021",
"027": "027",
"022": "022",
"036": "036",
"029": "029",
"028": "028",
"035": "035",
"056": "056",
"044": "044",
"034": "034",
"040": "040",
"039": "039",
"043": "043",
"051": "051",
"052": "052",
"059": "059",
"065": "065",
"061": "061",
"024": "024",
"068": "068",
"050": "050",
"078": "078",
"079": "079",
"067": "067",
"096": "096",
"099": "099",
"075": "075",
"081": "081",
"069": "069",
"073": "073",
"089": "089",
"090": "090",
"085": "085",
"088": "088",
"091": "091",
"093": "093",
"071": "071",
"095": "095",
"092": "092",
"094": "094"
}

View File

@@ -40,3 +40,7 @@
.read-the-docs {
color: #888;
}
section {
margin-bottom: 2rem;
}

View File

@@ -1,28 +1,100 @@
// src/App.tsx
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { MunicipioWidget } from './components/MunicipioWidget';
import { MunicipioSelector } from './components/MunicipioSelector';
import { getMunicipios, type MunicipioSimple } from './services/api';
import './App.css';
import { ResumenProvincialWidget } from './components/ResumenProvincialWidget';
import { BancasWidget } from './components/BancasWidget';
import { TelegramasView } from './components/TelegramasView';
import { MapaD3Widget } from './components/MapaD3Widget';
function App() {
const [selectedMunicipioId, setSelectedMunicipioId] = useState<string | null>(null);
const [listaMunicipios, setListaMunicipios] = useState<MunicipioSimple[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getMunicipios()
.then(setListaMunicipios)
.catch(err => console.error("Error al cargar la lista de municipios", err))
.finally(() => setLoading(false));
}, []);
if (loading) return <h1>Cargando datos iniciales...</h1>;
return (
<>
<h1>Elecciones 2025 - Resultados en Vivo</h1>
{/* Aquí podrías poner el widget del Resumen Provincial */}
<section>
<ResumenProvincialWidget distritoId="02" />
</section>
<hr />
<h2>Consulta por Municipio</h2>
<MunicipioSelector onMunicipioChange={setSelectedMunicipioId} />
{selectedMunicipioId && (
<div style={{ marginTop: '20px' }}>
<MunicipioWidget municipioId={selectedMunicipioId} />
<section style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: '20px' }}>
<div>
<h2>Mapa de Resultados</h2>
<MapaD3Widget
municipios={listaMunicipios}
onMunicipioClick={setSelectedMunicipioId}
/>
</div>
)}
<div>
<h2>Consulta por Municipio</h2>
<MunicipioSelector
municipios={listaMunicipios}
onMunicipioChange={setSelectedMunicipioId}
/>
{selectedMunicipioId && (
<div style={{ marginTop: '20px' }}>
<MunicipioWidget municipioId={selectedMunicipioId} />
</div>
)}
</div>
</section>
<section>
{/* Usamos el ID del distrito de Bs As ("02") */}
<ResumenProvincialWidget distritoId="02" />
</section>
<hr />
<section>
<h2>Consulta por Municipio</h2>
<MunicipioSelector onMunicipioChange={setSelectedMunicipioId} municipios={[]} />
{selectedMunicipioId && (
<div style={{ marginTop: '20px' }}>
<MunicipioWidget municipioId={selectedMunicipioId} />
</div>
)}
</section>
<section>
<h2>Proyección de Bancas</h2>
{/* Usamos el ID de la sección de La Plata ("0001") como ejemplo */}
<BancasWidget seccionId="0001" />
</section>
<hr />
<section>
<h2>Consulta de Resultados por Municipio</h2>
<MunicipioSelector onMunicipioChange={setSelectedMunicipioId} municipios={[]} />
{selectedMunicipioId && (
<div style={{ marginTop: '20px' }}>
<MunicipioWidget municipioId={selectedMunicipioId} />
</div>
)}
</section>
<hr />
<section>
<h2>Explorador de Telegramas</h2>
<TelegramasView />
</section>
</>
);
}

View File

@@ -0,0 +1,52 @@
// src/components/BancasWidget.tsx
import { useState, useEffect } from 'react';
import { getBancasPorSeccion, type ProyeccionBancas } from '../services/api';
interface Props {
seccionId: string;
}
export const BancasWidget = ({ seccionId }: Props) => {
const [data, setData] = useState<ProyeccionBancas | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const proyeccion = await getBancasPorSeccion(seccionId);
setData(proyeccion);
} catch (err) {
console.error("Error cargando bancas", err);
} finally {
setLoading(false);
}
};
fetchData();
}, [seccionId]);
if (loading) return <div>Cargando proyección de bancas...</div>;
if (!data) return <div>No hay datos de bancas disponibles.</div>;
return (
<div style={{ fontFamily: 'sans-serif', border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
<h3>Proyección de Bancas - {data.seccionNombre}</h3>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr>
<th style={{ textAlign: 'left' }}>Agrupación</th>
<th style={{ textAlign: 'right' }}>Bancas Obtenidas</th>
</tr>
</thead>
<tbody>
{data.proyeccion.map((partido) => (
<tr key={partido.agrupacionNombre}>
<td>{partido.agrupacionNombre}</td>
<td style={{ textAlign: 'right' }}>{partido.bancas}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

View File

@@ -0,0 +1,112 @@
import { useEffect, useRef, useState } from 'react';
import * as d3 from 'd3';
// FIX: Usamos 'import type' para los tipos y quitamos la importación de 'MunicipioSimple'
import type { FeatureCollection } from 'geojson';
// --- Interfaces y Constantes ---
interface MapaResultado {
municipioId: string;
agrupacionGanadoraId: string;
}
const COLOR_MAP: { [key: string]: string } = { "018": "#FFC107", "025": "#03A9F4", "031": "#4CAF50", "045": "#9C27B0", "default": "#E0E0E0" };
interface Props {
onMunicipioClick: (municipioId: string) => void;
}
export const MapaD3Widget = ({ onMunicipioClick }: Props) => {
const svgRef = useRef<SVGSVGElement | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const [tooltip, setTooltip] = useState<{ x: number; y: number; content: string } | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const svgElement = svgRef.current;
const containerElement = containerRef.current;
if (!svgElement || !containerElement) return;
const drawMap = (
geoData: FeatureCollection, // Usamos el tipo correcto
resultsMap: Map<string, MapaResultado>,
idMap: Record<string, string>
) => {
const { width, height } = containerElement.getBoundingClientRect();
if (width === 0 || height === 0) return;
const svg = d3.select(svgElement);
svg.selectAll('*').remove();
svg.attr('width', width).attr('height', height).attr('viewBox', `0 0 ${width} ${height}`);
const projection = d3.geoMercator().fitSize([width, height], geoData);
const pathGenerator = d3.geoPath().projection(projection);
const features = geoData.features;
svg.append('g')
.selectAll('path')
.data(features)
.join('path')
.attr('d', pathGenerator as any)
.attr('stroke', '#FFFFFF')
.attr('stroke-width', 0.5)
.attr('fill', (d: any) => {
const geoJsonId = d.properties.cca;
const apiId = idMap[geoJsonId];
const resultado = resultsMap.get(apiId);
return resultado ? COLOR_MAP[resultado.agrupacionGanadoraId] || COLOR_MAP.default : COLOR_MAP.default;
})
.style('cursor', 'pointer')
.on('click', (_, d: any) => {
const apiId = idMap[d.properties.cca];
if (apiId) onMunicipioClick(apiId);
})
.on('mouseover', (event, d: any) => {
d3.select(event.currentTarget).attr('stroke', 'black').attr('stroke-width', 2);
setTooltip({ x: event.pageX, y: event.pageY, content: d.properties.nam });
})
.on('mouseout', (event) => {
d3.select(event.currentTarget).attr('stroke', '#FFFFFF').attr('stroke-width', 0.5);
setTooltip(null);
});
};
(async () => {
try {
const [geoData, resultsData, idMap] = await Promise.all([
d3.json<FeatureCollection>('/buenos-aires-municipios.geojson'),
d3.json<MapaResultado[]>('http://localhost:5217/api/resultados/mapa'),
d3.json<Record<string, string>>('/municipioIdMap.json')
]);
if (geoData && resultsData && idMap) {
const resultsMap = new Map(resultsData.map(item => [item.municipioId, item]));
drawMap(geoData, resultsMap, idMap);
} else {
throw new Error("Faltan datos para renderizar el mapa.");
}
} catch (err) {
console.error("Error cargando datos para el mapa:", err);
} finally {
setLoading(false);
}
})();
}, [onMunicipioClick]);
if (loading) {
return <div ref={containerRef} style={{ width: '100%', height: '600px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>Cargando datos del mapa...</div>;
}
return (
<div ref={containerRef} style={{ width: '100%', height: '600px', border: '1px solid #eee' }}>
<svg ref={svgRef}></svg>
{tooltip && (
<div style={{
position: 'fixed', top: tooltip.y + 10, left: tooltip.x + 10,
backgroundColor: 'rgba(0, 0, 0, 0.75)', color: 'white', padding: '8px',
borderRadius: '4px', pointerEvents: 'none', fontSize: '14px', zIndex: 1000,
}}>
{tooltip.content}
</div>
)}
</div>
);
};

View File

@@ -1,31 +1,11 @@
// src/components/MunicipioSelector.tsx
import { useState, useEffect } from 'react';
import { getMunicipios, type MunicipioSimple } from '../services/api';
import { type MunicipioSimple } from '../services/api';
interface Props {
municipios: MunicipioSimple[];
onMunicipioChange: (municipioId: string) => void;
}
export const MunicipioSelector = ({ onMunicipioChange }: Props) => {
const [municipios, setMunicipios] = useState<MunicipioSimple[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadMunicipios = async () => {
try {
const data = await getMunicipios();
setMunicipios(data);
} catch (error) {
console.error("Error al cargar municipios", error);
} finally {
setLoading(false);
}
};
loadMunicipios();
}, []);
if (loading) return <p>Cargando municipios...</p>;
export const MunicipioSelector = ({ municipios, onMunicipioChange }: Props) => {
return (
<select onChange={(e) => onMunicipioChange(e.target.value)} defaultValue="">
<option value="" disabled>Seleccione un municipio</option>

View File

@@ -0,0 +1,52 @@
import { useState, useEffect } from 'react';
import { getResumenProvincial, type ResumenProvincial } from '../services/api';
interface Props {
distritoId: string;
}
export const ResumenProvincialWidget = ({ distritoId }: Props) => {
const [data, setData] = useState<ResumenProvincial | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const resumen = await getResumenProvincial(distritoId);
setData(resumen);
} catch (err) {
console.error("Error cargando resumen provincial", err);
} finally {
setLoading(false);
}
};
fetchData();
const intervalId = setInterval(fetchData, 15000); // Actualizamos cada 15s
return () => clearInterval(intervalId);
}, [distritoId]);
if (loading) return <div>Cargando resumen provincial...</div>;
if (!data) return <div>No hay datos provinciales disponibles.</div>;
return (
<div style={{ fontFamily: 'sans-serif', border: '1px solid #ccc', padding: '16px', borderRadius: '8px', backgroundColor: '#f9f9f9' }}>
<h2>Resumen Provincial - {data.provinciaNombre}</h2>
<p><strong>Mesas Escrutadas:</strong> {data.porcentajeEscrutado.toFixed(2)}% | <strong>Participación:</strong> {data.porcentajeParticipacion.toFixed(2)}%</p>
{data.resultados.map((partido) => (
<div key={partido.nombre} style={{ margin: '10px 0' }}>
<span>{partido.nombre}</span>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ backgroundColor: '#ddd', width: '100%', borderRadius: '4px', marginRight: '10px' }}>
<div style={{ width: `${partido.porcentaje}%`, backgroundColor: 'royalblue', color: 'white', padding: '4px', borderRadius: '4px', textAlign: 'right' }}>
<strong>{partido.porcentaje.toFixed(2)}%</strong>
</div>
</div>
<span>{partido.votos.toLocaleString('es-AR')}</span>
</div>
</div>
))}
</div>
);
};

View File

@@ -0,0 +1,65 @@
// src/components/TelegramasView.tsx
import { useState, useEffect } from 'react';
import { getListaTelegramas, getTelegramaPorId, type TelegramaDetalle } from '../services/api';
export const TelegramasView = () => {
const [listaIds, setListaIds] = useState<string[]>([]);
const [selectedTelegrama, setSelectedTelegrama] = useState<TelegramaDetalle | null>(null);
const [loadingList, setLoadingList] = useState(true);
const [loadingDetail, setLoadingDetail] = useState(false);
useEffect(() => {
const loadList = async () => {
try {
const ids = await getListaTelegramas();
setListaIds(ids);
} catch (error) {
console.error("Error al cargar lista de telegramas", error);
} finally {
setLoadingList(false);
}
};
loadList();
}, []);
const handleSelectTelegrama = async (mesaId: string) => {
try {
setLoadingDetail(true);
const detalle = await getTelegramaPorId(mesaId);
setSelectedTelegrama(detalle);
} catch (error) {
console.error(`Error al cargar telegrama ${mesaId}`, error);
} finally {
setLoadingDetail(false);
}
};
return (
<div style={{ display: 'flex', gap: '20px', height: '500px' }}>
<div style={{ flex: 1, border: '1px solid #ccc', overflowY: 'auto' }}>
<h4>Telegramas Disponibles</h4>
{loadingList ? <p>Cargando...</p> : (
<ul style={{ listStyle: 'none', padding: '10px' }}>
{listaIds.map(id => (
<li key={id} onClick={() => handleSelectTelegrama(id)} style={{ cursor: 'pointer', padding: '5px', borderBottom: '1px solid #eee' }}>
Mesa: {id}
</li>
))}
</ul>
)}
</div>
<div style={{ flex: 3, border: '1px solid #ccc', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
{loadingDetail ? <p>Cargando telegrama...</p> :
selectedTelegrama ? (
<iframe
src={`data:application/pdf;base64,${selectedTelegrama.contenidoBase64}`}
width="100%"
height="100%"
title={selectedTelegrama.id}
/>
) : <p>Seleccione un telegrama de la lista para visualizarlo.</p>
}
</div>
</div>
);
};

View File

@@ -39,6 +39,39 @@ export interface ResumenProvincial extends Omit<MunicipioResultados, 'municipioN
provinciaNombre: string;
}
export interface BancaResultado {
agrupacionNombre: string;
bancas: number;
}
export interface ProyeccionBancas {
seccionNombre: string;
proyeccion: BancaResultado[];
}
export interface TelegramaDetalle {
id: string;
ambitoGeograficoId: number;
contenidoBase64: string;
fechaEscaneo: string;
fechaTotalizacion: string;
}
export const getBancasPorSeccion = async (seccionId: string): Promise<ProyeccionBancas> => {
const response = await apiClient.get<ProyeccionBancas>(`/resultados/bancas/${seccionId}`);
return response.data;
};
export const getListaTelegramas = async (): Promise<string[]> => {
const response = await apiClient.get<string[]>('/telegramas');
return response.data;
};
export const getTelegramaPorId = async (mesaId: string): Promise<TelegramaDetalle> => {
const response = await apiClient.get<TelegramaDetalle>(`/telegramas/${mesaId}`);
return response.data;
};
export const getMunicipios = async (): Promise<MunicipioSimple[]> => {
const response = await apiClient.get<MunicipioSimple[]>('/catalogos/municipios');
return response.data;

View File

@@ -113,4 +113,78 @@ public class ResultadosController : ControllerBase
return Ok(await Task.FromResult(respuestaSimulada));
}
[HttpGet("bancas/{seccionId}")]
public async Task<IActionResult> GetBancasPorSeccion(string seccionId)
{
// 1. Buscamos el ámbito de la sección electoral
var seccion = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.SeccionId == seccionId && a.NivelId == 4); // Nivel 4 = Sección Electoral
if (seccion == null)
{
return NotFound(new { message = $"No se encontró la sección electoral con ID {seccionId}" });
}
// 2. Buscamos todas las proyecciones para ese ámbito, incluyendo el nombre de la agrupación
var proyecciones = await _dbContext.ProyeccionesBancas
.AsNoTracking()
.Include(p => p.AgrupacionPolitica) // Incluimos el nombre del partido
.Where(p => p.AmbitoGeograficoId == seccion.Id)
.Select(p => new
{
// Creamos un objeto anónimo para la respuesta, más limpio que un DTO para este caso simple
AgrupacionNombre = p.AgrupacionPolitica.Nombre,
Bancas = p.NroBancas
})
.OrderByDescending(p => p.Bancas)
.ToListAsync();
if (!proyecciones.Any())
{
return NotFound(new { message = $"No se han encontrado proyecciones de bancas para la sección {seccion.Nombre}" });
}
// 3. Devolvemos un objeto que contiene el nombre de la sección y la lista de resultados
return Ok(new
{
SeccionNombre = seccion.Nombre,
Proyeccion = proyecciones
});
}
[HttpGet("mapa")]
public async Task<IActionResult> GetResultadosParaMapa()
{
// Esta consulta es mucho más eficiente y se traduce bien a SQL.
// Paso 1: Para cada ámbito, encontrar la cantidad máxima de votos.
var maxVotosPorAmbito = _dbContext.ResultadosVotos
.GroupBy(rv => rv.AmbitoGeograficoId)
.Select(g => new
{
AmbitoId = g.Key,
MaxVotos = g.Max(v => v.CantidadVotos)
});
// Paso 2: Unir los resultados originales con los máximos para encontrar el registro ganador.
// Esto nos da, para cada ámbito, el registro completo del partido que tuvo más votos.
var resultadosGanadores = await _dbContext.ResultadosVotos
.Join(
maxVotosPorAmbito,
voto => new { AmbitoId = voto.AmbitoGeograficoId, Votos = voto.CantidadVotos },
max => new { AmbitoId = max.AmbitoId, Votos = max.MaxVotos },
(voto, max) => voto // Nos quedamos con el objeto 'ResultadoVoto' completo
)
.Include(rv => rv.AmbitoGeografico) // Incluimos el ámbito para obtener el MunicipioId
.Where(rv => rv.AmbitoGeografico.MunicipioId != null)
.Select(rv => new
{
MunicipioId = rv.AmbitoGeografico.MunicipioId,
AgrupacionGanadoraId = rv.AgrupacionPoliticaId
})
.ToListAsync();
return Ok(resultadosGanadores);
}
}

View File

@@ -0,0 +1,62 @@
using Elecciones.Core.DTOs.ApiResponses;
using Elecciones.Database;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elecciones.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class TelegramasController : ControllerBase
{
private readonly EleccionesDbContext _dbContext;
public TelegramasController(EleccionesDbContext dbContext)
{
_dbContext = dbContext;
}
/// <summary>
/// Obtiene la lista de IDs de todos los telegramas que han sido totalizados y descargados.
/// </summary>
[HttpGet]
public async Task<IActionResult> GetListaTelegramas()
{
var ids = await _dbContext.Telegramas
.AsNoTracking()
.OrderBy(t => t.Id)
.Select(t => t.Id)
.ToListAsync();
return Ok(ids);
}
/// <summary>
/// Obtiene el contenido completo de un telegrama específico, incluyendo la imagen en Base64.
/// </summary>
/// <param name="mesaId">El ID único de la mesa/telegrama (ej. "0200100001M").</param>
[HttpGet("{mesaId}")]
public async Task<IActionResult> GetTelegramaPorId(string mesaId)
{
var telegrama = await _dbContext.Telegramas
.AsNoTracking()
.FirstOrDefaultAsync(t => t.Id == mesaId);
if (telegrama == null)
{
return NotFound(new { message = $"No se encontró el telegrama con ID {mesaId}" });
}
// Devolvemos todos los datos del telegrama
return Ok(new
{
telegrama.Id,
telegrama.AmbitoGeograficoId,
telegrama.ContenidoBase64,
telegrama.FechaEscaneo,
telegrama.FechaTotalizacion
});
}
}

View File

@@ -14,6 +14,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
</ItemGroup>

View File

@@ -1,30 +1,26 @@
using Elecciones.Database;
using Microsoft.EntityFrameworkCore;
using Serilog;
// Esta es la estructura estándar y recomendada.
var builder = WebApplication.CreateBuilder(args);
// --- 1. Configuración de Servicios ---
// 1. Configurar Serilog. Esta es la forma correcta de integrarlo.
builder.Host.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logs/api-.log", rollingInterval: RollingInterval.Day));
// Añade la cadena de conexión y el DbContext para Entity Framework Core.
// 2. Añadir servicios al contenedor.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<EleccionesDbContext>(options =>
options.UseSqlServer(connectionString));
// Añade los servicios para los controladores de la API.
builder.Services.AddControllers();
// Configura CORS para permitir que tu frontend (y www.eldia.com) consuman la API.
// builder.Services.AddCors(options =>
// {
// options.AddDefaultPolicy(policy =>
// {
// policy.WithOrigins("http://localhost:5173", "http://localhost:8600", "http://www.eldia.com", "http://elecciones2025.eldia.com")
// .AllowAnyHeader()
// .AllowAnyMethod();
// });
// });
var allowedOrigins = builder.Configuration["AllowedOrigins"]?.Split(',') ?? [];
var allowedOrigins = builder.Configuration["AllowedOrigins"]?.Split(',') ?? Array.Empty<string>();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
@@ -38,34 +34,28 @@ builder.Services.AddCors(options =>
});
});
// Añade la configuración de Swagger/OpenAPI para la documentación de la API.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// 3. Construir la aplicación.
var app = builder.Build();
// --- 2. Configuración del Pipeline de Peticiones HTTP ---
// 4. Configurar el pipeline de peticiones HTTP.
// Añadimos el logging de peticiones de Serilog aquí.
app.UseSerilogRequestLogging();
// Habilita la UI de Swagger en el entorno de desarrollo.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// Redirige las peticiones HTTP a HTTPS.
app.UseHttpsRedirection();
// Usa la política de CORS que definimos arriba.
app.UseCors();
// Habilita la autorización (lo configuraremos si es necesario más adelante).
app.UseAuthorization();
// Mapea las rutas a los controladores de la API.
app.MapControllers();
// Inicia la aplicación.
// 5. Ejecutar la aplicación.
// El try/catch/finally se puede omitir; el logging de Serilog ya se encarga de los errores fatales.
app.Run();

View File

@@ -13,6 +13,8 @@
"Microsoft.AspNetCore.OpenApi": "9.0.5",
"Microsoft.EntityFrameworkCore.SqlServer": "9.0.8",
"Microsoft.EntityFrameworkCore.Tools": "9.0.8",
"Serilog.AspNetCore": "9.0.0",
"Serilog.Sinks.File": "7.0.0",
"Swashbuckle.AspNetCore": "9.0.3"
},
"runtime": {
@@ -626,6 +628,20 @@
}
}
},
"Microsoft.Extensions.FileProviders.Abstractions/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Hosting.Abstractions/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "9.0.8",
"Microsoft.Extensions.FileProviders.Abstractions": "9.0.0",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.Http/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
@@ -840,6 +856,115 @@
}
}
},
"Serilog/4.2.0": {
"runtime": {
"lib/net9.0/Serilog.dll": {
"assemblyVersion": "4.2.0.0",
"fileVersion": "4.2.0.0"
}
}
},
"Serilog.AspNetCore/9.0.0": {
"dependencies": {
"Serilog": "4.2.0",
"Serilog.Extensions.Hosting": "9.0.0",
"Serilog.Formatting.Compact": "3.0.0",
"Serilog.Settings.Configuration": "9.0.0",
"Serilog.Sinks.Console": "6.0.0",
"Serilog.Sinks.Debug": "3.0.0",
"Serilog.Sinks.File": "7.0.0"
},
"runtime": {
"lib/net9.0/Serilog.AspNetCore.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Extensions.Hosting/9.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Hosting.Abstractions": "9.0.0",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Serilog": "4.2.0",
"Serilog.Extensions.Logging": "9.0.0"
},
"runtime": {
"lib/net9.0/Serilog.Extensions.Hosting.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Extensions.Logging/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Logging": "9.0.8",
"Serilog": "4.2.0"
},
"runtime": {
"lib/net9.0/Serilog.Extensions.Logging.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Formatting.Compact/3.0.0": {
"dependencies": {
"Serilog": "4.2.0"
},
"runtime": {
"lib/net8.0/Serilog.Formatting.Compact.dll": {
"assemblyVersion": "3.0.0.0",
"fileVersion": "3.0.0.0"
}
}
},
"Serilog.Settings.Configuration/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
"Microsoft.Extensions.DependencyModel": "9.0.8",
"Serilog": "4.2.0"
},
"runtime": {
"lib/net9.0/Serilog.Settings.Configuration.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Sinks.Console/6.0.0": {
"dependencies": {
"Serilog": "4.2.0"
},
"runtime": {
"lib/net8.0/Serilog.Sinks.Console.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
}
}
},
"Serilog.Sinks.Debug/3.0.0": {
"dependencies": {
"Serilog": "4.2.0"
},
"runtime": {
"lib/net8.0/Serilog.Sinks.Debug.dll": {
"assemblyVersion": "3.0.0.0",
"fileVersion": "3.0.0.0"
}
}
},
"Serilog.Sinks.File/7.0.0": {
"dependencies": {
"Serilog": "4.2.0"
},
"runtime": {
"lib/net9.0/Serilog.Sinks.File.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.0.0"
}
}
},
"Swashbuckle.AspNetCore/9.0.3": {
"dependencies": {
"Microsoft.Extensions.ApiDescription.Server": "9.0.0",
@@ -1426,6 +1551,20 @@
"path": "microsoft.extensions.diagnostics.abstractions/9.0.8",
"hashPath": "microsoft.extensions.diagnostics.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.FileProviders.Abstractions/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-uK439QzYR0q2emLVtYzwyK3x+T5bTY4yWsd/k/ZUS9LR6Sflp8MIdhGXW8kQCd86dQD4tLqvcbLkku8qHY263Q==",
"path": "microsoft.extensions.fileproviders.abstractions/9.0.0",
"hashPath": "microsoft.extensions.fileproviders.abstractions.9.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Hosting.Abstractions/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yUKJgu81ExjvqbNWqZKshBbLntZMbMVz/P7Way2SBx7bMqA08Mfdc9O7hWDKAiSp+zPUGT6LKcSCQIPeDK+CCw==",
"path": "microsoft.extensions.hosting.abstractions/9.0.0",
"hashPath": "microsoft.extensions.hosting.abstractions.9.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Http/9.0.8": {
"type": "package",
"serviceable": true,
@@ -1566,6 +1705,69 @@
"path": "mono.texttemplating/3.0.0",
"hashPath": "mono.texttemplating.3.0.0.nupkg.sha512"
},
"Serilog/4.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-gmoWVOvKgbME8TYR+gwMf7osROiWAURterc6Rt2dQyX7wtjZYpqFiA/pY6ztjGQKKV62GGCyOcmtP1UKMHgSmA==",
"path": "serilog/4.2.0",
"hashPath": "serilog.4.2.0.nupkg.sha512"
},
"Serilog.AspNetCore/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-JslDajPlBsn3Pww1554flJFTqROvK9zz9jONNQgn0D8Lx2Trw8L0A8/n6zEQK1DAZWXrJwiVLw8cnTR3YFuYsg==",
"path": "serilog.aspnetcore/9.0.0",
"hashPath": "serilog.aspnetcore.9.0.0.nupkg.sha512"
},
"Serilog.Extensions.Hosting/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-u2TRxuxbjvTAldQn7uaAwePkWxTHIqlgjelekBtilAGL5sYyF3+65NWctN4UrwwGLsDC7c3Vz3HnOlu+PcoxXg==",
"path": "serilog.extensions.hosting/9.0.0",
"hashPath": "serilog.extensions.hosting.9.0.0.nupkg.sha512"
},
"Serilog.Extensions.Logging/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==",
"path": "serilog.extensions.logging/9.0.0",
"hashPath": "serilog.extensions.logging.9.0.0.nupkg.sha512"
},
"Serilog.Formatting.Compact/3.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==",
"path": "serilog.formatting.compact/3.0.0",
"hashPath": "serilog.formatting.compact.3.0.0.nupkg.sha512"
},
"Serilog.Settings.Configuration/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-4/Et4Cqwa+F88l5SeFeNZ4c4Z6dEAIKbu3MaQb2Zz9F/g27T5a3wvfMcmCOaAiACjfUb4A6wrlTVfyYUZk3RRQ==",
"path": "serilog.settings.configuration/9.0.0",
"hashPath": "serilog.settings.configuration.9.0.0.nupkg.sha512"
},
"Serilog.Sinks.Console/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==",
"path": "serilog.sinks.console/6.0.0",
"hashPath": "serilog.sinks.console.6.0.0.nupkg.sha512"
},
"Serilog.Sinks.Debug/3.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-4BzXcdrgRX7wde9PmHuYd9U6YqycCC28hhpKonK7hx0wb19eiuRj16fPcPSVp0o/Y1ipJuNLYQ00R3q2Zs8FDA==",
"path": "serilog.sinks.debug/3.0.0",
"hashPath": "serilog.sinks.debug.3.0.0.nupkg.sha512"
},
"Serilog.Sinks.File/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fKL7mXv7qaiNBUC71ssvn/dU0k9t0o45+qm2XgKAlSt19xF+ijjxyA3R6HmCgfKEKwfcfkwWjayuQtRueZFkYw==",
"path": "serilog.sinks.file/7.0.0",
"hashPath": "serilog.sinks.file.7.0.0.nupkg.sha512"
},
"Swashbuckle.AspNetCore/9.0.3": {
"type": "package",
"serviceable": true,

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1d580231134cc923bf8cbc524140ceb0ae88752f")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -173,3 +173,13 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Diagnostics.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Http.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Options.ConfigurationExtensions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\buenos-aires-municipios.geojson
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.AspNetCore.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Extensions.Hosting.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Extensions.Logging.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Formatting.Compact.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Settings.Configuration.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Sinks.Console.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Sinks.Debug.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Sinks.File.dll

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","r5wfLIFTEo2\u002Bd7Tsx44bFIb0SPNdPvg4KbBRNmbWVFA="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","/FHWuH7ftxc5u992j4YhVijd0fBiiQLWe\u002BV0ZlveX5c="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","r5wfLIFTEo2\u002Bd7Tsx44bFIb0SPNdPvg4KbBRNmbWVFA="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","/FHWuH7ftxc5u992j4YhVijd0fBiiQLWe\u002BV0ZlveX5c="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"O7YawHw32G/Fh2bs+snZgm9O7okI0WYgTQmXM931znY=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"O7YawHw32G/Fh2bs+snZgm9O7okI0WYgTQmXM931znY=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -71,6 +71,14 @@
"target": "Package",
"version": "[9.0.8, )"
},
"Serilog.AspNetCore": {
"target": "Package",
"version": "[9.0.0, )"
},
"Serilog.Sinks.File": {
"target": "Package",
"version": "[7.0.0, )"
},
"Swashbuckle.AspNetCore": {
"target": "Package",
"version": "[9.0.3, )"

View File

@@ -3,10 +3,10 @@
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)system.text.json\9.0.8\buildTransitive\net8.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\9.0.8\buildTransitive\net8.0\System.Text.Json.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\9.0.0\build\Microsoft.Extensions.ApiDescription.Server.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\9.0.0\build\Microsoft.Extensions.ApiDescription.Server.targets')" />
<Import Project="$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets" Condition="Exists('$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
<Import Project="$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets" Condition="Exists('$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets')" />
</ImportGroup>
</Project>

View File

@@ -0,0 +1,20 @@
// src/Elecciones.Core/DTOs/BancaDto.cs
using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs;
public class BancaDto
{
[JsonPropertyName("idAgrupacion")]
public string IdAgrupacion { get; set; } = null!;
[JsonPropertyName("nombreAgrupacion")]
public string NombreAgrupacion { get; set; } = null!;
[JsonPropertyName("nroBancas")]
public int NroBancas { get; set; }
public class RepartoBancasDto
{
[JsonPropertyName("repartoBancas")]
public List<BancaDto> RepartoBancas { get; set; } = [];
}
}

View File

@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs;
public class CategoriaDto
{
[JsonPropertyName("orden")]
public int Orden { get; set; }
[JsonPropertyName("categoriald")]
public int CategoriaId { get; set; }
[JsonPropertyName("nombre")]
public string Nombre { get; set; } = null!;
}

View File

@@ -1,4 +1,3 @@
// src/Elecciones.Core/DTOs/EstadoRecuentoDto.cs
using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs;
@@ -11,8 +10,14 @@ public class EstadoRecuentoDto
[JsonPropertyName("mesasTotalizadas")]
public int MesasTotalizadas { get; set; }
[JsonPropertyName("mesasTotalizadasPorcentaje")]
public decimal MesasTotalizadasPorcentaje { get; set; }
[JsonPropertyName("cantidadElectores")]
public int CantidadElectores { get; set; }
[JsonPropertyName("cantidadVotantes")]
public int CantidadVotantes { get; set; }
[JsonPropertyName("participacionPorcentaje")]
public decimal ParticipacionPorcentaje { get; set; }

View File

@@ -0,0 +1,8 @@
using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs;
// Reutilizamos el DTO de estado de recuento que ya teníamos,
// pero le damos un alias para claridad si es necesario.
// A nivel de estructura son idénticos.
public class EstadoRecuentoGeneralDto : EstadoRecuentoDto { }

View File

@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
using System.Collections.Generic;
namespace Elecciones.Core.DTOs;
public class ResumenDto
{
[JsonPropertyName("valoresTotalizadosPositivos")]
public List<ResumenPositivoDto> ValoresTotalizadosPositivos { get; set; } = [];
}
public class ResumenPositivoDto
{
[JsonPropertyName("idAgrupacion")]
public string IdAgrupacion { get; set; } = null!;
[JsonPropertyName("votos")]
public long Votos { get; set; }
[JsonPropertyName("votosPorcentaje")]
public decimal VotosPorcentaje { get; set; }
}

View File

@@ -0,0 +1,19 @@
// src/Elecciones.Core/DTOs/TelegramaFileDto.cs
using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs;
public class TelegramaFileDto
{
[JsonPropertyName("nombreArchivo")]
public string NombreArchivo { get; set; } = null!;
[JsonPropertyName("imagen")]
public string Imagen { get; set; } = null!;
[JsonPropertyName("fechaEscaneo")]
public string FechaEscaneo { get; set; } = null!;
[JsonPropertyName("fechaTotalizacion")]
public string FechaTotalizacion { get; set; } = null!;
}

View File

@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
"Elecciones.Core/1.0.0": {
"runtime": {
"Elecciones.Core.dll": {}
}
}
}
},
"libraries": {
"Elecciones.Core/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b90baadeedb870b5b1c9eeeb7022a0d211b61bec")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generado por la clase WriteCodeFragment de MSBuild.

View File

@@ -0,0 +1,15 @@
is_global = true
build_property.TargetFramework = net9.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Elecciones.Core
build_property.ProjectDir = E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,8 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@@ -0,0 +1,11 @@
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\bin\Release\net9.0\Elecciones.Core.deps.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\bin\Release\net9.0\Elecciones.Core.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\bin\Release\net9.0\Elecciones.Core.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\Elecciones.Core.GeneratedMSBuildEditorConfig.editorconfig
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\Elecciones.Core.AssemblyInfoInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\Elecciones.Core.AssemblyInfo.cs
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\Elecciones.Core.csproj.CoreCompileInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\Elecciones.Core.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\refint\Elecciones.Core.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\Elecciones.Core.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Core\obj\Release\net9.0\ref\Elecciones.Core.dll

View File

@@ -10,7 +10,11 @@ public class EleccionesDbContext(DbContextOptions<EleccionesDbContext> options)
public DbSet<AmbitoGeografico> AmbitosGeograficos { get; set; }
public DbSet<ResultadoVoto> ResultadosVotos { get; set; }
public DbSet<EstadoRecuento> EstadosRecuentos { get; set; }
// Podríamos añadir más tablas como CategoriaElectoral o ProyeccionBanca aquí
public DbSet<ProyeccionBanca> ProyeccionesBancas { get; set; }
public DbSet<Telegrama> Telegramas { get; set; }
public DbSet<ResumenVoto> ResumenesVotos { get; set; }
public DbSet<EstadoRecuentoGeneral> EstadosRecuentosGenerales { get; set; }
public DbSet<CategoriaElectoral> CategoriasElectorales { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -27,5 +31,13 @@ public class EleccionesDbContext(DbContextOptions<EleccionesDbContext> options)
entity.Property(e => e.MesasTotalizadasPorcentaje).HasPrecision(5, 2);
entity.Property(e => e.ParticipacionPorcentaje).HasPrecision(5, 2);
});
modelBuilder.Entity<ResumenVoto>()
.Property(e => e.VotosPorcentaje).HasPrecision(5, 2);
modelBuilder.Entity<EstadoRecuentoGeneral>(entity =>
{
entity.Property(e => e.MesasTotalizadasPorcentaje).HasPrecision(5, 2);
entity.Property(e => e.ParticipacionPorcentaje).HasPrecision(5, 2);
});
}
}

View File

@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elecciones.Database.Entities;
public class CategoriaElectoral
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] // La API nos da el ID, no la BD.
public int Id { get; set; } // Corresponde a 'categoriald'
[Required]
public string Nombre { get; set; } = null!;
public int Orden { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elecciones.Database.Entities;
public class EstadoRecuentoGeneral
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] // Le dice a EF que no genere este valor.
public int AmbitoGeograficoId { get; set; }
public int MesasEsperadas { get; set; }
public int MesasTotalizadas { get; set; }
public decimal MesasTotalizadasPorcentaje { get; set; }
public int CantidadElectores { get; set; }
public int CantidadVotantes { get; set; }
public decimal ParticipacionPorcentaje { get; set; }
}

View File

@@ -0,0 +1,25 @@
// src/Elecciones.Database/Entities/ProyeccionBanca.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elecciones.Database.Entities;
public class ProyeccionBanca
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// El ámbito donde se proyecta (ej. Sección Electoral)
public int AmbitoGeograficoId { get; set; }
[ForeignKey("AmbitoGeograficoId")]
public AmbitoGeografico AmbitoGeografico { get; set; } = null!;
// La agrupación que obtiene la banca
public string AgrupacionPoliticaId { get; set; } = null!;
[ForeignKey("AgrupacionPoliticaId")]
public AgrupacionPolitica AgrupacionPolitica { get; set; } = null!;
// Cantidad de bancas obtenidas
public int NroBancas { get; set; }
}

View File

@@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elecciones.Database.Entities;
public class ResumenVoto
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// El ámbito donde se resume (siempre provincial en este caso)
public int AmbitoGeograficoId { get; set; }
public string AgrupacionPoliticaId { get; set; } = null!;
public long Votos { get; set; }
public decimal VotosPorcentaje { get; set; }
}

View File

@@ -0,0 +1,21 @@
// src/Elecciones.Database/Entities/Telegrama.cs
using System.ComponentModel.DataAnnotations;
namespace Elecciones.Database.Entities;
public class Telegrama
{
// El ID único del telegrama (ej. "0202600060X") será nuestra Primary Key.
[Key]
public string Id { get; set; } = null!;
// En qué ámbito fue escrutado
public int AmbitoGeograficoId { get; set; }
// El contenido del telegrama en Base64
public string ContenidoBase64 { get; set; } = null!;
public DateTime FechaEscaneo { get; set; }
public DateTime FechaTotalizacion { get; set; }
}

View File

@@ -12,8 +12,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Elecciones.Database.Migrations
{
[DbContext(typeof(EleccionesDbContext))]
[Migration("20250814161142_InitialCreate")]
partial class InitialCreate
[Migration("20250815181913_InitialSchema")]
partial class InitialSchema
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -126,6 +126,63 @@ namespace Elecciones.Database.Migrations
b.ToTable("EstadosRecuentos");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
{
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CantidadElectores")
.HasColumnType("int");
b.Property<int>("CantidadVotantes")
.HasColumnType("int");
b.Property<int>("MesasEsperadas")
.HasColumnType("int");
b.Property<int>("MesasTotalizadas")
.HasColumnType("int");
b.Property<decimal>("MesasTotalizadasPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<decimal>("ParticipacionPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("AmbitoGeograficoId");
b.ToTable("EstadosRecuentosGenerales");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("NroBancas")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId");
b.ToTable("ProyeccionesBancas");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.Property<long>("Id")
@@ -154,6 +211,56 @@ namespace Elecciones.Database.Migrations
b.ToTable("ResultadosVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<long>("Votos")
.HasColumnType("bigint");
b.Property<decimal>("VotosPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("Id");
b.ToTable("ResumenesVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.Telegrama", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<string>("ContenidoBase64")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("FechaEscaneo")
.HasColumnType("datetime2");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.HasKey("Id");
b.ToTable("Telegramas");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
{
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
@@ -165,6 +272,25 @@ namespace Elecciones.Database.Migrations
b.Navigation("AmbitoGeografico");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
.WithMany()
.HasForeignKey("AgrupacionPoliticaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
.WithMany()
.HasForeignKey("AmbitoGeograficoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AgrupacionPolitica");
b.Navigation("AmbitoGeografico");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")

View File

@@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Elecciones.Database.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
public partial class InitialSchema : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
@@ -45,6 +45,54 @@ namespace Elecciones.Database.Migrations
table.PrimaryKey("PK_AmbitosGeograficos", x => x.Id);
});
migrationBuilder.CreateTable(
name: "EstadosRecuentosGenerales",
columns: table => new
{
AmbitoGeograficoId = table.Column<int>(type: "int", nullable: false),
MesasEsperadas = table.Column<int>(type: "int", nullable: false),
MesasTotalizadas = table.Column<int>(type: "int", nullable: false),
MesasTotalizadasPorcentaje = table.Column<decimal>(type: "decimal(5,2)", precision: 5, scale: 2, nullable: false),
CantidadElectores = table.Column<int>(type: "int", nullable: false),
CantidadVotantes = table.Column<int>(type: "int", nullable: false),
ParticipacionPorcentaje = table.Column<decimal>(type: "decimal(5,2)", precision: 5, scale: 2, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EstadosRecuentosGenerales", x => x.AmbitoGeograficoId);
});
migrationBuilder.CreateTable(
name: "ResumenesVotos",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AmbitoGeograficoId = table.Column<int>(type: "int", nullable: false),
AgrupacionPoliticaId = table.Column<string>(type: "nvarchar(max)", nullable: false),
Votos = table.Column<long>(type: "bigint", nullable: false),
VotosPorcentaje = table.Column<decimal>(type: "decimal(5,2)", precision: 5, scale: 2, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ResumenesVotos", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Telegramas",
columns: table => new
{
Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
AmbitoGeograficoId = table.Column<int>(type: "int", nullable: false),
ContenidoBase64 = table.Column<string>(type: "nvarchar(max)", nullable: false),
FechaEscaneo = table.Column<DateTime>(type: "datetime2", nullable: false),
FechaTotalizacion = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Telegramas", x => x.Id);
});
migrationBuilder.CreateTable(
name: "EstadosRecuentos",
columns: table => new
@@ -72,6 +120,33 @@ namespace Elecciones.Database.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ProyeccionesBancas",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AmbitoGeograficoId = table.Column<int>(type: "int", nullable: false),
AgrupacionPoliticaId = table.Column<string>(type: "nvarchar(450)", nullable: false),
NroBancas = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProyeccionesBancas", x => x.Id);
table.ForeignKey(
name: "FK_ProyeccionesBancas_AgrupacionesPoliticas_AgrupacionPoliticaId",
column: x => x.AgrupacionPoliticaId,
principalTable: "AgrupacionesPoliticas",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ProyeccionesBancas_AmbitosGeograficos_AmbitoGeograficoId",
column: x => x.AmbitoGeograficoId,
principalTable: "AmbitosGeograficos",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ResultadosVotos",
columns: table => new
@@ -99,6 +174,16 @@ namespace Elecciones.Database.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ProyeccionesBancas_AgrupacionPoliticaId",
table: "ProyeccionesBancas",
column: "AgrupacionPoliticaId");
migrationBuilder.CreateIndex(
name: "IX_ProyeccionesBancas_AmbitoGeograficoId",
table: "ProyeccionesBancas",
column: "AmbitoGeograficoId");
migrationBuilder.CreateIndex(
name: "IX_ResultadosVotos_AgrupacionPoliticaId",
table: "ResultadosVotos",
@@ -117,9 +202,21 @@ namespace Elecciones.Database.Migrations
migrationBuilder.DropTable(
name: "EstadosRecuentos");
migrationBuilder.DropTable(
name: "EstadosRecuentosGenerales");
migrationBuilder.DropTable(
name: "ProyeccionesBancas");
migrationBuilder.DropTable(
name: "ResultadosVotos");
migrationBuilder.DropTable(
name: "ResumenesVotos");
migrationBuilder.DropTable(
name: "Telegramas");
migrationBuilder.DropTable(
name: "AgrupacionesPoliticas");

View File

@@ -0,0 +1,332 @@
// <auto-generated />
using System;
using Elecciones.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Elecciones.Database.Migrations
{
[DbContext(typeof(EleccionesDbContext))]
[Migration("20250815183610_AddCategoriasElectorales")]
partial class AddCategoriasElectorales
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("IdTelegrama")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("AgrupacionesPoliticas");
});
modelBuilder.Entity("Elecciones.Database.Entities.AmbitoGeografico", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CircuitoId")
.HasColumnType("nvarchar(max)");
b.Property<string>("DistritoId")
.HasColumnType("nvarchar(max)");
b.Property<string>("EstablecimientoId")
.HasColumnType("nvarchar(max)");
b.Property<string>("MesaId")
.HasColumnType("nvarchar(max)");
b.Property<string>("MunicipioId")
.HasColumnType("nvarchar(max)");
b.Property<int>("NivelId")
.HasColumnType("int");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SeccionId")
.HasColumnType("nvarchar(max)");
b.Property<string>("SeccionProvincialId")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("AmbitosGeograficos");
});
modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Orden")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("CategoriasElectorales");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
{
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CantidadElectores")
.HasColumnType("int");
b.Property<int>("CantidadVotantes")
.HasColumnType("int");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.Property<int>("MesasEsperadas")
.HasColumnType("int");
b.Property<int>("MesasTotalizadas")
.HasColumnType("int");
b.Property<decimal>("MesasTotalizadasPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<decimal>("ParticipacionPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<long>("VotosEnBlanco")
.HasColumnType("bigint");
b.Property<long>("VotosNulos")
.HasColumnType("bigint");
b.Property<long>("VotosRecurridos")
.HasColumnType("bigint");
b.HasKey("AmbitoGeograficoId");
b.ToTable("EstadosRecuentos");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
{
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CantidadElectores")
.HasColumnType("int");
b.Property<int>("CantidadVotantes")
.HasColumnType("int");
b.Property<int>("MesasEsperadas")
.HasColumnType("int");
b.Property<int>("MesasTotalizadas")
.HasColumnType("int");
b.Property<decimal>("MesasTotalizadasPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<decimal>("ParticipacionPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("AmbitoGeograficoId");
b.ToTable("EstadosRecuentosGenerales");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("NroBancas")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId");
b.ToTable("ProyeccionesBancas");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<long>("CantidadVotos")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId", "AgrupacionPoliticaId")
.IsUnique();
b.ToTable("ResultadosVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<long>("Votos")
.HasColumnType("bigint");
b.Property<decimal>("VotosPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("Id");
b.ToTable("ResumenesVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.Telegrama", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<string>("ContenidoBase64")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("FechaEscaneo")
.HasColumnType("datetime2");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.HasKey("Id");
b.ToTable("Telegramas");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
{
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
.WithMany()
.HasForeignKey("AmbitoGeograficoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AmbitoGeografico");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
.WithMany()
.HasForeignKey("AgrupacionPoliticaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
.WithMany()
.HasForeignKey("AmbitoGeograficoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AgrupacionPolitica");
b.Navigation("AmbitoGeografico");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
.WithMany()
.HasForeignKey("AgrupacionPoliticaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
.WithMany()
.HasForeignKey("AmbitoGeograficoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AgrupacionPolitica");
b.Navigation("AmbitoGeografico");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Elecciones.Database.Migrations
{
/// <inheritdoc />
public partial class AddCategoriasElectorales : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CategoriasElectorales",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false),
Nombre = table.Column<string>(type: "nvarchar(max)", nullable: false),
Orden = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CategoriasElectorales", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CategoriasElectorales");
}
}
}

View File

@@ -81,6 +81,23 @@ namespace Elecciones.Database.Migrations
b.ToTable("AmbitosGeograficos");
});
modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Orden")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("CategoriasElectorales");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
{
b.Property<int>("AmbitoGeograficoId")
@@ -123,6 +140,63 @@ namespace Elecciones.Database.Migrations
b.ToTable("EstadosRecuentos");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
{
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CantidadElectores")
.HasColumnType("int");
b.Property<int>("CantidadVotantes")
.HasColumnType("int");
b.Property<int>("MesasEsperadas")
.HasColumnType("int");
b.Property<int>("MesasTotalizadas")
.HasColumnType("int");
b.Property<decimal>("MesasTotalizadasPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<decimal>("ParticipacionPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("AmbitoGeograficoId");
b.ToTable("EstadosRecuentosGenerales");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("NroBancas")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId");
b.ToTable("ProyeccionesBancas");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.Property<long>("Id")
@@ -151,6 +225,56 @@ namespace Elecciones.Database.Migrations
b.ToTable("ResultadosVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<long>("Votos")
.HasColumnType("bigint");
b.Property<decimal>("VotosPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("Id");
b.ToTable("ResumenesVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.Telegrama", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<string>("ContenidoBase64")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("FechaEscaneo")
.HasColumnType("datetime2");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.HasKey("Id");
b.ToTable("Telegramas");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
{
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
@@ -162,6 +286,25 @@ namespace Elecciones.Database.Migrations
b.Navigation("AmbitoGeografico");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
.WithMany()
.HasForeignKey("AgrupacionPoliticaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
.WithMany()
.HasForeignKey("AmbitoGeograficoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AgrupacionPolitica");
b.Navigation("AmbitoGeografico");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")

View File

@@ -0,0 +1,14 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b90baadeedb870b5b1c9eeeb7022a0d211b61bec")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generado por la clase WriteCodeFragment de MSBuild.

View File

@@ -0,0 +1,23 @@
is_global = true
build_property.TargetFramework = net9.0
build_property.TargetFramework = net9.0
build_property.TargetPlatformMinVersion =
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Elecciones.Database
build_property.ProjectDir = E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,8 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@@ -0,0 +1,14 @@
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\bin\Release\net9.0\Elecciones.Database.deps.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\bin\Release\net9.0\Elecciones.Database.runtimeconfig.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\bin\Release\net9.0\Elecciones.Database.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\bin\Release\net9.0\Elecciones.Database.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.csproj.AssemblyReference.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.GeneratedMSBuildEditorConfig.editorconfig
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.AssemblyInfoInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.AssemblyInfo.cs
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.csproj.CoreCompileInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\refint\Elecciones.Database.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\Elecciones.Database.genruntimeconfig.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Database\obj\Release\net9.0\ref\Elecciones.Database.dll

View File

@@ -9,6 +9,12 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
<None Update="buenos-aires-municipios.geojson">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using static Elecciones.Core.DTOs.BancaDto;
namespace Elecciones.Infrastructure.Services;
@@ -18,6 +19,8 @@ public class ElectoralApiService : IElectoralApiService
_configuration = configuration;
}
// --- MÉTODOS DE LA INTERFAZ ---
public async Task<string?> GetAuthTokenAsync()
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
@@ -67,20 +70,84 @@ public class ElectoralApiService : IElectoralApiService
public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
// Construimos la URL con todos los parámetros requeridos. Usamos categoría 5 (Diputados) como ejemplo.
var requestUri = $"/api/resultados/getResultados?distritold={distritoId}&seccionld={seccionId}&municipiold={municipioId}&categoriald=5";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<ResultadosDto>()
: null;
}
if (!response.IsSuccessStatusCode)
{
return null;
}
public async Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getBancas?distritold={distritoId}&seccionld={seccionId}&categoriald=5";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
return await response.Content.ReadFromJsonAsync<ResultadosDto>();
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<RepartoBancasDto>()
: null;
}
public async Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getTelegramasTotalizados?distritold={distritoId}&seccionld={seccionId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<List<string[]>>()
: null;
}
public async Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getFile?mesald={mesaId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<TelegramaFileDto>()
: null;
}
public async Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getResumen?distritold={distritoId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<ResumenDto>() : null;
}
public async Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/estados/estadoRecuento?distritold={distritoId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<EstadoRecuentoGeneralDto>() : null;
}
public async Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var request = new HttpRequestMessage(HttpMethod.Get, "/api/catalogo/getCategorias");
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<List<CategoriaDto>>()
: null;
}
}

View File

@@ -1,101 +1,145 @@
// src/Elecciones.Infrastructure/Services/FakeElectoralApiService.cs
using Elecciones.Core.DTOs;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using static Elecciones.Core.DTOs.BancaDto;
namespace Elecciones.Infrastructure.Services;
public class FakeElectoralApiService : IElectoralApiService
{
private readonly ILogger<FakeElectoralApiService> _logger;
private List<AmbitoDto>? _fakeAmbitosCache = null;
private readonly Random _random = new Random();
public FakeElectoralApiService(ILogger<FakeElectoralApiService> logger)
{
_logger = logger;
}
private void GenerateFakeAmbitosFromGeoJson()
{
if (_fakeAmbitosCache != null) return;
_logger.LogWarning("--- USANDO SERVICIO FALSO (FAKE) ---");
_logger.LogInformation("Generando datos de prueba de ámbitos desde el archivo GeoJSON...");
var geoJsonPath = Path.Combine(AppContext.BaseDirectory, "buenos-aires-municipios.geojson");
if (!File.Exists(geoJsonPath))
{
_logger.LogError("No se encontró el archivo buenos-aires-municipios.geojson.");
_fakeAmbitosCache = new List<AmbitoDto>();
return;
}
var geoJsonString = File.ReadAllText(geoJsonPath);
using var document = JsonDocument.Parse(geoJsonString);
var features = document.RootElement.GetProperty("features").EnumerateArray().ToList();
var ambitos = new List<AmbitoDto>();
ambitos.Add(new AmbitoDto { NivelId = 10, Nombre = "BUENOS AIRES", CodigoAmbitos = new CodigoAmbitoDto { DistritoId = "02" } });
var secciones = new List<AmbitoDto> {
new() { NivelId = 4, Nombre = "PRIMERA SECCION ELECTORAL", CodigoAmbitos = new CodigoAmbitoDto { DistritoId = "02", SeccionId = "0001" } },
new() { NivelId = 4, Nombre = "SEGUNDA SECCION ELECTORAL", CodigoAmbitos = new CodigoAmbitoDto { DistritoId = "02", SeccionId = "0002" } },
new() { NivelId = 4, Nombre = "TERCERA SECCION ELECTORAL", CodigoAmbitos = new CodigoAmbitoDto { DistritoId = "02", SeccionId = "0003" } }
};
ambitos.AddRange(secciones);
for (int i = 0; i < features.Count; i++)
{
var feature = features[i];
var properties = feature.GetProperty("properties");
var seccionAsignada = secciones[i % secciones.Count];
ambitos.Add(new AmbitoDto { NivelId = 5, Nombre = properties.GetProperty("nam").GetString() ?? "Sin Nombre", CodigoAmbitos = new CodigoAmbitoDto { MunicipioId = properties.GetProperty("cca").GetString(), DistritoId = "02", SeccionId = seccionAsignada.CodigoAmbitos.SeccionId } });
}
_fakeAmbitosCache = ambitos;
_logger.LogInformation("Se generaron {count} ámbitos de prueba (Provincia, Secciones y Municipios).", _fakeAmbitosCache.Count);
}
public Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId)
{
_logger.LogInformation("Simulando obtención de Resumen para distrito {DistritoId}...", distritoId);
var resumen = new ResumenDto
{
ValoresTotalizadosPositivos = new List<ResumenPositivoDto>
{
new() { IdAgrupacion = "025", Votos = 2500000 + _random.Next(1000), VotosPorcentaje = 45.12m },
new() { IdAgrupacion = "018", Votos = 2100000 + _random.Next(1000), VotosPorcentaje = 38.78m },
new() { IdAgrupacion = "031", Votos = 800000 + _random.Next(1000), VotosPorcentaje = 14.10m }
}
};
return Task.FromResult<ResumenDto?>(resumen);
}
public Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId)
{
_logger.LogInformation("Simulando obtención de Estado de Recuento General para distrito {DistritoId}...", distritoId);
var estado = new EstadoRecuentoGeneralDto
{
MesasEsperadas = 38000,
MesasTotalizadas = _random.Next(28000, 37000),
MesasTotalizadasPorcentaje = 95.5m,
CantidadElectores = 12500000,
CantidadVotantes = 9375000,
ParticipacionPorcentaje = 75.0m
};
return Task.FromResult<EstadoRecuentoGeneralDto?>(estado);
}
public Task<string?> GetAuthTokenAsync()
{
_logger.LogWarning("--- USANDO SERVICIO FALSO (FAKE) ---");
_logger.LogInformation("Simulando obtención de token...");
string fakeToken = "FAKE_TOKEN_FOR_DEVELOPMENT";
return Task.FromResult<string?>(fakeToken);
return Task.FromResult<string?>("FAKE_TOKEN_FOR_DEVELOPMENT");
}
public Task<List<CatalogoDto>?> GetCatalogoCompletoAsync(string authToken)
{
_logger.LogInformation("Simulando obtención de Catálogo Completo...");
var catalogo = new List<CatalogoDto>
{
new() // Simulamos el catálogo para la categoría de Diputados (ID 5)
{
Version = 1,
CategoriaId = 5,
Ambitos =
[
new() { NivelId = 10, Nombre = "BUENOS AIRES", CodigoAmbitos = new() { DistritoId = "02" } },
new() { NivelId = 5, Nombre = "LA PLATA", CodigoAmbitos = new() { DistritoId = "02", SeccionId = "0001", MunicipioId = "056" } },
new() { NivelId = 5, Nombre = "MAR DEL PLATA", CodigoAmbitos = new() { DistritoId = "02", SeccionId = "0005", MunicipioId = "035" } }
],
Niveles =
[
new() { NivelId = 10, Nombre = "Provincia" },
new() { NivelId = 5, Nombre = "Municipio" }
]
}
};
GenerateFakeAmbitosFromGeoJson();
var catalogo = new List<CatalogoDto> { new() { Version = 1, CategoriaId = 5, Ambitos = _fakeAmbitosCache ?? new List<AmbitoDto>(), Niveles = new List<NivelDto> { new() { NivelId = 10, Nombre = "Provincia" }, new() { NivelId = 4, Nombre = "Seccion" }, new() { NivelId = 5, Nombre = "Municipio" } } } };
return Task.FromResult<List<CatalogoDto>?>(catalogo);
}
public Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId)
{
_logger.LogInformation("Simulando obtención de Agrupaciones Políticas para el distrito {Distrito} y categoría {Categoria}...", distritoId, categoriaId);
var agrupaciones = new List<AgrupacionDto>
{
new() { IdAgrupacion = "018", IdAgrupacionTelegrama = "131", NombreAgrupacion = "FRENTE DE AVANZADA" },
new() { IdAgrupacion = "025", IdAgrupacionTelegrama = "132", NombreAgrupacion = "ALIANZA POR EL FUTURO" },
new() { IdAgrupacion = "031", IdAgrupacionTelegrama = "133", NombreAgrupacion = "UNION POPULAR" },
new() { IdAgrupacion = "045", IdAgrupacionTelegrama = "134", NombreAgrupacion = "PARTIDO VECINALISTA" }
};
var agrupaciones = new List<AgrupacionDto> { new() { IdAgrupacion = "018", IdAgrupacionTelegrama = "131", NombreAgrupacion = "FRENTE DE AVANZADA" }, new() { IdAgrupacion = "025", IdAgrupacionTelegrama = "132", NombreAgrupacion = "ALIANZA POR EL FUTURO" }, new() { IdAgrupacion = "031", IdAgrupacionTelegrama = "133", NombreAgrupacion = "UNION POPULAR" }, new() { IdAgrupacion = "045", IdAgrupacionTelegrama = "134", NombreAgrupacion = "PARTIDO VECINALISTA" } };
return Task.FromResult<List<AgrupacionDto>?>(agrupaciones);
}
public Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId)
{
_logger.LogInformation("Simulando obtención de Resultados para el municipio {MunicipioId}...", municipioId);
// YA NO FILTRAMOS POR ID. DEVOLVEMOS DATOS SIMULADOS PARA CUALQUIER MUNICIPIO.
var random = new Random();
var resultados = new ResultadosDto
{
FechaTotalizacion = DateTime.Now.ToString("o"),
EstadoRecuento = new EstadoRecuentoDto
{
MesasEsperadas = random.Next(100, 2000), // Hacemos que varíe
MesasTotalizadas = random.Next(50, 100),
CantidadElectores = random.Next(50000, 600000),
ParticipacionPorcentaje = random.Next(60, 85) + (decimal)random.NextDouble()
},
ValoresTotalizadosPositivos =
[
// Usamos los IDs reales de nuestro catálogo de agrupaciones falsas
new() { IdAgrupacion = "018", NombreAgrupacion = "FRENTE DE AVANZADA", Votos = random.Next(10000, 20000) },
new() { IdAgrupacion = "025", NombreAgrupacion = "ALIANZA POR EL FUTURO", Votos = random.Next(15000, 25000) },
new() { IdAgrupacion = "031", NombreAgrupacion = "UNION POPULAR", Votos = random.Next(5000, 10000) },
new() { IdAgrupacion = "045", NombreAgrupacion = "PARTIDO VECINALISTA", Votos = random.Next(2000, 5000) }
],
ValoresTotalizadosOtros = new VotosOtrosDto
{
VotosEnBlanco = random.Next(1000, 2000),
VotosNulos = random.Next(500, 1000),
VotosRecurridos = random.Next(20, 50)
}
};
var resultados = new ResultadosDto { FechaTotalizacion = DateTime.Now.ToString("o"), EstadoRecuento = new EstadoRecuentoDto { MesasEsperadas = _random.Next(100, 2000), MesasTotalizadas = _random.Next(50, 100), CantidadElectores = _random.Next(50000, 600000), ParticipacionPorcentaje = _random.Next(60, 85) + (decimal)_random.NextDouble() }, ValoresTotalizadosPositivos = new List<VotosPositivosDto> { new() { IdAgrupacion = "018", Votos = _random.Next(10000, 20000) }, new() { IdAgrupacion = "025", Votos = _random.Next(15000, 25000) }, new() { IdAgrupacion = "031", Votos = _random.Next(5000, 10000) }, new() { IdAgrupacion = "045", Votos = _random.Next(2000, 5000) } }, ValoresTotalizadosOtros = new VotosOtrosDto { VotosEnBlanco = _random.Next(1000, 2000), VotosNulos = _random.Next(500, 1000), VotosRecurridos = _random.Next(20, 50) } };
return Task.FromResult<ResultadosDto?>(resultados);
}
public Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId)
{
var reparto = new RepartoBancasDto { RepartoBancas = new List<BancaDto> { new() { IdAgrupacion = "025", NroBancas = _random.Next(5, 9) }, new() { IdAgrupacion = "018", NroBancas = _random.Next(3, 7) } } };
return Task.FromResult<RepartoBancasDto?>(reparto);
}
public Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId)
{
var lista = new List<string[]> { new[] { $"02{seccionId}0001M" }, new[] { $"02{seccionId}0002M" } };
return Task.FromResult<List<string[]>?>(lista);
}
public Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId)
{
var file = new TelegramaFileDto { NombreArchivo = mesaId, Imagen = "FAKE_BASE64_PDF_CONTENT", FechaEscaneo = DateTime.UtcNow.AddMinutes(-10).ToString("o"), FechaTotalizacion = DateTime.UtcNow.ToString("o") };
return Task.FromResult<TelegramaFileDto?>(file);
}
public Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken)
{
_logger.LogInformation("Simulando obtención de Categorías Electorales...");
var categorias = new List<CategoriaDto>
{
new() { CategoriaId = 5, Nombre = "DIPUTADOS NACIONALES", Orden = 1 },
new() { CategoriaId = 6, Nombre = "SENADORES NACIONALES", Orden = 2 }
};
return Task.FromResult<List<CategoriaDto>?>(categorias);
}
}

View File

@@ -1,12 +1,24 @@
// src/Elecciones.Infrastructure/Services/IElectoralApiService.cs
using Elecciones.Core.DTOs;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Elecciones.Core.DTOs.BancaDto;
namespace Elecciones.Infrastructure.Services;
public interface IElectoralApiService
{
Task<string?> GetAuthTokenAsync();
// Métodos para catálogos
Task<List<CatalogoDto>?> GetCatalogoCompletoAsync(string authToken);
Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId);
// Métodos para resultados y datos dinámicos
Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId);
Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId);
Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId);
Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId);
Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId);
Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId);
Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,285 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
"Elecciones.Infrastructure/1.0.0": {
"dependencies": {
"Elecciones.Core": "1.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8"
},
"runtime": {
"Elecciones.Infrastructure.dll": {}
}
},
"Microsoft.Extensions.Configuration/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Configuration.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Configuration.Abstractions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Configuration.Binder/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Configuration.Binder.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.DependencyInjection/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/9.0.8": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Diagnostics/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "9.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Diagnostics.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Diagnostics.Abstractions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Http/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Diagnostics": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Http.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Logging/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Options/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Primitives/9.0.8": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.Primitives.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Elecciones.Core/1.0.0": {
"runtime": {
"Elecciones.Core.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
}
}
},
"libraries": {
"Elecciones.Infrastructure/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.Extensions.Configuration/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-6m+8Xgmf8UWL0p/oGqBM+0KbHE5/ePXbV1hKXgC59zEv0aa0DW5oiiyxDbK5kH5j4gIvyD5uWL0+HadKBJngvQ==",
"path": "microsoft.extensions.configuration/9.0.8",
"hashPath": "microsoft.extensions.configuration.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yNou2KM35RvzOh4vUFtl2l33rWPvOCoba+nzEDJ+BgD8aOL/jew4WPCibQvntRfOJ2pJU8ARygSMD+pdjvDHuA==",
"path": "microsoft.extensions.configuration.abstractions/9.0.8",
"hashPath": "microsoft.extensions.configuration.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Binder/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-0vK9DnYrYChdiH3yRZWkkp4x4LbrfkWEdBc5HOsQ8t/0CLOWKXKkkhOE8A1shlex0hGydbGrhObeypxz/QTm+w==",
"path": "microsoft.extensions.configuration.binder/9.0.8",
"hashPath": "microsoft.extensions.configuration.binder.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-JJjI2Fa+QtZcUyuNjbKn04OjIUX5IgFGFu/Xc+qvzh1rXdZHLcnqqVXhR4093bGirTwacRlHiVg1XYI9xum6QQ==",
"path": "microsoft.extensions.dependencyinjection/9.0.8",
"hashPath": "microsoft.extensions.dependencyinjection.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xY3lTjj4+ZYmiKIkyWitddrp1uL5uYiweQjqo4BKBw01ZC4HhcfgLghDpPZcUlppgWAFqFy9SgkiYWOMx365pw==",
"path": "microsoft.extensions.dependencyinjection.abstractions/9.0.8",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Diagnostics/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BKkLCFXzJvNmdngeYBf72VXoZqTJSb1orvjdzDLaGobicoGFBPW8ug2ru1nnEewMEwJzMgnsjHQY8EaKWmVhKg==",
"path": "microsoft.extensions.diagnostics/9.0.8",
"hashPath": "microsoft.extensions.diagnostics.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Diagnostics.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UDY7blv4DCyIJ/8CkNrQKLaAZFypXQavRZ2DWf/2zi1mxYYKKw2t8AOCBWxNntyPZHPGhtEmL3snFM98ADZqTw==",
"path": "microsoft.extensions.diagnostics.abstractions/9.0.8",
"hashPath": "microsoft.extensions.diagnostics.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Http/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-jDj+4aDByk47oESlDDTtk6LWzlXlmoCsjCn6ihd+i9OntN885aPLszUII5+w0B/7wYSZcS3KdjqLAIhKLSiBXQ==",
"path": "microsoft.extensions.http/9.0.8",
"hashPath": "microsoft.extensions.http.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Logging/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Z/7ze+0iheT7FJeZPqJKARYvyC2bmwu3whbm/48BJjdlGVvgDguoCqJIkI/67NkroTYobd5geai1WheNQvWrgA==",
"path": "microsoft.extensions.logging/9.0.8",
"hashPath": "microsoft.extensions.logging.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pYnAffJL7ARD/HCnnPvnFKSIHnTSmWz84WIlT9tPeQ4lHNiu0Az7N/8itihWvcF8sT+VVD5lq8V+ckMzu4SbOw==",
"path": "microsoft.extensions.logging.abstractions/9.0.8",
"hashPath": "microsoft.extensions.logging.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Options/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OmTaQ0v4gxGQkehpwWIqPoEiwsPuG/u4HUsbOFoWGx4DKET2AXzopnFe/fE608FIhzc/kcg2p8JdyMRCCUzitQ==",
"path": "microsoft.extensions.options/9.0.8",
"hashPath": "microsoft.extensions.options.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Options.ConfigurationExtensions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eW2s6n06x0w6w4nsX+SvpgsFYkl+Y0CttYAt6DKUXeqprX+hzNqjSfOh637fwNJBg7wRBrOIRHe49gKiTgJxzQ==",
"path": "microsoft.extensions.options.configurationextensions/9.0.8",
"hashPath": "microsoft.extensions.options.configurationextensions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-tizSIOEsIgSNSSh+hKeUVPK7xmTIjR8s+mJWOu1KXV3htvNQiPMFRMO17OdI1y/4ZApdBVk49u/08QGC9yvLug==",
"path": "microsoft.extensions.primitives/9.0.8",
"hashPath": "microsoft.extensions.primitives.9.0.8.nupkg.sha512"
},
"Elecciones.Core/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b90baadeedb870b5b1c9eeeb7022a0d211b61bec")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -13,3 +13,4 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\refint\Elecciones.Infrastructure.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\Elecciones.Infrastructure.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\ref\Elecciones.Infrastructure.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Debug\net9.0\buenos-aires-municipios.geojson

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generado por la clase WriteCodeFragment de MSBuild.

View File

@@ -0,0 +1,15 @@
is_global = true
build_property.TargetFramework = net9.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Elecciones.Infrastructure
build_property.ProjectDir = E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,8 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@@ -0,0 +1,16 @@
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Release\net9.0\buenos-aires-municipios.geojson
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Release\net9.0\Elecciones.Infrastructure.deps.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Release\net9.0\Elecciones.Infrastructure.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Release\net9.0\Elecciones.Infrastructure.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Release\net9.0\Elecciones.Core.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Release\net9.0\Elecciones.Core.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.csproj.AssemblyReference.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.GeneratedMSBuildEditorConfig.editorconfig
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.AssemblyInfoInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.AssemblyInfo.cs
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.csproj.CoreCompileInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Eleccion.B7F7B2EF.Up2Date
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\refint\Elecciones.Infrastructure.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\Elecciones.Infrastructure.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Release\net9.0\ref\Elecciones.Infrastructure.dll

View File

@@ -11,6 +11,10 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,23 +2,37 @@ using Elecciones.Database;
using Elecciones.Infrastructure.Services;
using Elecciones.Worker;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System.Net.Http;
using System.Net.Security;
using System.Security.Authentication;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
Log.Information("Iniciando Elecciones.Worker Host...");
var builder = Host.CreateApplicationBuilder(args);
// --- Configuración de Servicios ---
builder.Services.AddSerilog(config =>
config
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logs/worker-.log", rollingInterval: RollingInterval.Day));
// 1. Configuración de Base de Datos (¡Este bloque es esencial!)
// --- Configuración de Servicios ---
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<EleccionesDbContext>(options =>
options.UseSqlServer(connectionString));
// 2. Configuración del Servicio de API (elegirá el Real o el Falso según el modo de compilación)
#if DEBUG
// En modo DEBUG (desarrollo local), usamos el servicio FALSO.
// No es necesario registrar el ILogger, .NET lo inyecta automáticamente.
builder.Services.AddSingleton<IElectoralApiService, FakeElectoralApiService>();
#else
// En modo RELEASE (producción), usamos el servicio REAL.
// --- SECCIÓN MODIFICADA (FINAL) ---
builder.Services.AddHttpClient("ElectoralApiClient", client =>
{
var baseUrl = builder.Configuration["ElectoralApi:BaseUrl"];
@@ -26,13 +40,44 @@ builder.Services.AddHttpClient("ElectoralApiClient", client =>
{
client.BaseAddress = new Uri(baseUrl);
}
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
// Forzamos el protocolo TLS 1.3
EnabledSslProtocols = SslProtocols.Tls13,
// --- ¡¡¡LA LÍNEA CLAVE CORREGIDA!!! ---
// Forzamos explícitamente los únicos 3 cipher suites que el servidor acepta.
CipherSuitesPolicy = new CipherSuitesPolicy(new[]
{
TlsCipherSuite.TLS_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256
})
}
};
});
builder.Services.AddSingleton<IElectoralApiService, ElectoralApiService>();
// --- FIN DE LA SECCIÓN MODIFICADA ---
#endif
// 3. Registrar el Worker como un servicio que se ejecuta en segundo plano.
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
host.Run();
try {
host.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "El Host de Elecciones.Worker terminó inesperadamente");
}
finally
{
Log.CloseAndFlush();
}

View File

@@ -28,33 +28,36 @@ public class Worker : BackgroundService
{
_logger.LogInformation("Elecciones Worker iniciado a las: {time}", DateTimeOffset.Now);
// --- 1. SINCRONIZACIÓN INICIAL DE CATÁLOGOS ---
// Se ejecuta una sola vez al inicio para poblar las tablas maestras.
await SincronizarCatalogosAsync(stoppingToken);
// --- 2. BUCLE DE SONDEO DE RESULTADOS ---
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Iniciando sondeo periódico de resultados...");
_logger.LogInformation("-------------------------------------------------");
while (!stoppingToken.IsCancellationRequested)
{
await SondearResultadosAsync(stoppingToken);
// Ejecutamos todos los sondeos en paralelo para mayor eficiencia
await Task.WhenAll(
SondearResultadosAsync(stoppingToken),
SondearBancasAsync(stoppingToken),
SondearTelegramasAsync(stoppingToken),
SondearResumenProvincialAsync(stoppingToken),
SondearEstadoRecuentoGeneralAsync(stoppingToken)
);
try
{
// Esperamos 10 segundos antes de la siguiente consulta.
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
catch (TaskCanceledException)
{
// Es normal que esto ocurra cuando se detiene la aplicación.
break;
// Esperamos 1 minuto antes del siguiente ciclo completo de sondeos
_logger.LogInformation("Ciclo de sondeo completado. Esperando 1 minuto para el siguiente...");
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
catch (TaskCanceledException) { break; }
}
_logger.LogInformation("Elecciones Worker se está deteniendo.");
}
private async Task ObtenerTokenSiEsNecesario(CancellationToken stoppingToken)
{
// En un futuro, se podría añadir lógica para renovar el token solo cuando expire.
@@ -114,7 +117,6 @@ public class Worker : BackgroundService
}
}
// NUEVO MÉTODO AUXILIAR PARA GUARDAR LOS DATOS
private async Task GuardarResultadosDeAmbitoAsync(EleccionesDbContext dbContext, int ambitoId, Elecciones.Core.DTOs.ResultadosDto resultados, CancellationToken stoppingToken)
{
// --- ACTUALIZAR O INSERTAR ESTADO RECUENTO ---
@@ -168,66 +170,359 @@ public class Worker : BackgroundService
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
_logger.LogInformation("Limpiando tablas maestras para la nueva ingesta...");
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResultadosVotos", stoppingToken);
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM EstadosRecuentos", stoppingToken);
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM AgrupacionesPoliticas", stoppingToken);
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM AmbitosGeograficos", stoppingToken);
// --- 1. INGESTA DE ÁMBITOS GEOGRÁFICOS ---
var catalogos = await _apiService.GetCatalogoCompletoAsync(_authToken);
if (catalogos is { Count: > 0 })
// --- 1. SINCRONIZACIÓN DE CATEGORÍAS (NUEVO) ---
var categoriasApi = await _apiService.GetCategoriasAsync(_authToken);
if (categoriasApi is { Count: > 0 })
{
foreach (var ambitoDto in catalogos.SelectMany(c => c.Ambitos))
var categoriasEnDb = await dbContext.CategoriasElectorales.ToDictionaryAsync(c => c.Id, c => c, stoppingToken);
foreach (var categoriaDto in categoriasApi)
{
var nuevoAmbito = new AmbitoGeografico
if (categoriasEnDb.TryGetValue(categoriaDto.CategoriaId, out var categoriaExistente))
{
Nombre = ambitoDto.Nombre,
NivelId = ambitoDto.NivelId,
DistritoId = ambitoDto.CodigoAmbitos.DistritoId,
SeccionId = ambitoDto.CodigoAmbitos.SeccionId,
MunicipioId = ambitoDto.CodigoAmbitos.MunicipioId,
SeccionProvincialId = ambitoDto.CodigoAmbitos.SeccionProvincialId
};
await dbContext.AmbitosGeograficos.AddAsync(nuevoAmbito, stoppingToken);
categoriaExistente.Nombre = categoriaDto.Nombre;
categoriaExistente.Orden = categoriaDto.Orden;
}
else
{
await dbContext.CategoriasElectorales.AddAsync(new CategoriaElectoral
{
Id = categoriaDto.CategoriaId,
Nombre = categoriaDto.Nombre,
Orden = categoriaDto.Orden
}, stoppingToken);
}
}
_logger.LogInformation("Datos de Ámbitos Geográficos listos para ser guardados.");
_logger.LogInformation("Sincronización de Categorías Electorales completada.");
}
else
{
_logger.LogWarning("No se recibieron datos del catálogo de Ámbitos.");
_logger.LogWarning("No se recibieron datos del catálogo de Categorías.");
}
// --- 2. INGESTA DE AGRUPACIONES POLÍTICAS ---
var agrupaciones = await _apiService.GetAgrupacionesAsync(_authToken, "02", 5);
if (agrupaciones is { Count: > 0 })
// --- 2. SINCRONIZACIÓN DE ÁMBITOS GEOGRÁFICOS ---
var catalogoAmbitosApi = await _apiService.GetCatalogoCompletoAsync(_authToken);
if (catalogoAmbitosApi is { Count: > 0 })
{
foreach (var agrupacionDto in agrupaciones)
// Cargamos los ámbitos existentes de la BD en un diccionario para búsqueda rápida
var ambitosEnDb = await dbContext.AmbitosGeograficos
.ToDictionaryAsync(a => a.MunicipioId ?? a.SeccionId ?? a.DistritoId ?? a.Nombre, a => a, stoppingToken);
foreach (var ambitoDto in catalogoAmbitosApi.SelectMany(c => c.Ambitos))
{
var nuevaAgrupacion = new AgrupacionPolitica
// Usamos una clave única para identificar el ámbito (ej. ID de municipio)
var claveUnica = ambitoDto.CodigoAmbitos.MunicipioId ?? ambitoDto.CodigoAmbitos.SeccionId ?? ambitoDto.CodigoAmbitos.DistritoId ?? ambitoDto.Nombre;
if (ambitosEnDb.TryGetValue(claveUnica, out var ambitoExistente))
{
Id = agrupacionDto.IdAgrupacion,
IdTelegrama = agrupacionDto.IdAgrupacionTelegrama,
Nombre = agrupacionDto.NombreAgrupacion
};
await dbContext.AgrupacionesPoliticas.AddAsync(nuevaAgrupacion, stoppingToken);
// El ámbito ya existe, actualizamos sus datos descriptivos.
// No actualizamos los IDs, ya que forman parte de la identidad del ámbito.
ambitoExistente.Nombre = ambitoDto.Nombre;
ambitoExistente.NivelId = ambitoDto.NivelId;
}
else
{
// El ámbito es nuevo, lo añadimos (el código de inserción que ya tenemos es correcto)
var nuevoAmbito = new AmbitoGeografico
{
Nombre = ambitoDto.Nombre,
NivelId = ambitoDto.NivelId,
DistritoId = ambitoDto.CodigoAmbitos.DistritoId,
SeccionId = ambitoDto.CodigoAmbitos.SeccionId,
MunicipioId = ambitoDto.CodigoAmbitos.MunicipioId,
SeccionProvincialId = ambitoDto.CodigoAmbitos.SeccionProvincialId
};
await dbContext.AmbitosGeograficos.AddAsync(nuevoAmbito, stoppingToken);
}
}
_logger.LogInformation("Datos de Agrupaciones Políticas listos para ser guardados.");
_logger.LogInformation("Sincronización de Ámbitos Geográficos completada.");
}
else
{
_logger.LogWarning("No se recibieron datos del catálogo de Agrupaciones.");
_logger.LogWarning("No se recibieron datos del catálogo de Ámbitos. Los datos existentes no serán modificados.");
}
// --- 3. GUARDADO FINAL EN LA BASE DE DATOS ---
int registrosGuardados = await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("{count} registros totales han sido guardados en la base de datos.", registrosGuardados);
// --- 3. SINCRONIZACIÓN DE AGRUPACIONES POLÍTICAS ---
var agrupacionesApi = await _apiService.GetAgrupacionesAsync(_authToken, "02", 5);
if (agrupacionesApi is { Count: > 0 })
{
var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas
.ToDictionaryAsync(a => a.Id, a => a, stoppingToken);
_logger.LogInformation("Sincronización de catálogos completada exitosamente.");
foreach (var agrupacionDto in agrupacionesApi)
{
if (agrupacionesEnDb.TryGetValue(agrupacionDto.IdAgrupacion, out var agrupacionExistente))
{
// La agrupación ya existe, actualizamos el nombre por si cambia
agrupacionExistente.Nombre = agrupacionDto.NombreAgrupacion;
agrupacionExistente.IdTelegrama = agrupacionDto.IdAgrupacionTelegrama;
}
else
{
// La agrupación es nueva, la añadimos
var nuevaAgrupacion = new AgrupacionPolitica
{
Id = agrupacionDto.IdAgrupacion,
IdTelegrama = agrupacionDto.IdAgrupacionTelegrama,
Nombre = agrupacionDto.NombreAgrupacion
};
await dbContext.AgrupacionesPoliticas.AddAsync(nuevaAgrupacion, stoppingToken);
}
}
_logger.LogInformation("Sincronización de Agrupaciones Políticas completada.");
}
else
{
_logger.LogWarning("No se recibieron datos del catálogo de Agrupaciones. Los datos existentes no serán modificados.");
}
// --- 4. GUARDADO FINAL ---
int cambiosGuardados = await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("{count} cambios en los catálogos han sido guardados en la base de datos.", cambiosGuardados);
_logger.LogInformation("Sincronización de catálogos maestros finalizada.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error CRÍTICO durante la sincronización de catálogos. El worker podría no funcionar correctamente.");
_logger.LogError(ex, "Ocurrió un error CRÍTICO durante la sincronización de catálogos.");
}
}
private async Task SondearBancasAsync(CancellationToken stoppingToken)
{
try
{
await ObtenerTokenSiEsNecesario(stoppingToken);
if (string.IsNullOrEmpty(_authToken) || stoppingToken.IsCancellationRequested) return;
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
// Obtenemos las secciones electorales (donde se reparten bancas de diputados)
var secciones = await dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 4 && a.DistritoId != null && a.SeccionId != null) // Nivel 4 = Sección Electoral
.ToListAsync(stoppingToken);
if (!secciones.Any())
{
_logger.LogWarning("No se encontraron ámbitos de tipo 'Sección Electoral' en la BD para sondear bancas.");
return;
}
_logger.LogInformation("Iniciando sondeo de Bancas para {count} secciones...", secciones.Count);
// Esta bandera nos asegura que solo borramos la tabla una vez y solo si hay datos nuevos.
bool hasReceivedNewData = false;
foreach (var seccion in secciones)
{
if (stoppingToken.IsCancellationRequested) break;
var repartoBancas = await _apiService.GetBancasAsync(_authToken, seccion.DistritoId!, seccion.SeccionId!);
// Verificamos que la respuesta no sea nula y que la lista de bancas contenga al menos un elemento.
if (repartoBancas?.RepartoBancas is { Count: > 0 })
{
// Si esta es la primera vez en este ciclo de sondeo que recibimos datos válidos,
// borramos todos los datos viejos de la tabla.
if (!hasReceivedNewData)
{
_logger.LogInformation("Se recibieron nuevos datos de bancas. Limpiando tabla de proyecciones...");
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken);
hasReceivedNewData = true; // Marcamos que ya hemos limpiado la tabla.
}
// Procedemos a añadir las nuevas proyecciones a la sesión de EF Core.
foreach (var banca in repartoBancas.RepartoBancas)
{
var nuevaProyeccion = new ProyeccionBanca
{
AmbitoGeograficoId = seccion.Id,
AgrupacionPoliticaId = banca.IdAgrupacion,
NroBancas = banca.NroBancas
};
await dbContext.ProyeccionesBancas.AddAsync(nuevaProyeccion, stoppingToken);
}
}
else
{
_logger.LogWarning("No se recibieron datos de bancas para la sección {seccionId}.", seccion.SeccionId);
}
}
// Si hemos añadido alguna entidad nueva (es decir, hasReceivedNewData es true),
// guardamos todos los cambios en la base de datos.
if (hasReceivedNewData)
{
await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("Sondeo de Bancas completado. La tabla de proyecciones ha sido actualizada.");
}
else
{
_logger.LogInformation("Sondeo de Bancas completado. No se encontraron datos nuevos, la tabla no fue modificada.");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error en el sondeo de Bancas.");
}
}
private async Task SondearTelegramasAsync(CancellationToken stoppingToken)
{
try
{
await ObtenerTokenSiEsNecesario(stoppingToken);
if (string.IsNullOrEmpty(_authToken) || stoppingToken.IsCancellationRequested) return;
// --- CADA SONDEO USA SU PROPIO DBCONTEXT FRESCO ---
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var secciones = await dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 4 && a.DistritoId != null && a.SeccionId != null)
.ToListAsync(stoppingToken);
if (!secciones.Any()) return;
_logger.LogInformation("Iniciando sondeo de Telegramas nuevos...");
foreach (var seccion in secciones)
{
if (stoppingToken.IsCancellationRequested) break;
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(_authToken, seccion.DistritoId!, seccion.SeccionId!);
if (listaTelegramasApi is { Count: > 0 })
{
var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList();
// --- LÓGICA DE DUPLICADOS ---
// Consultamos a la base de datos por los IDs que la API nos acaba de dar
var idsYaEnDb = await dbContext.Telegramas
.Where(t => idsDeApi.Contains(t.Id))
.Select(t => t.Id)
.ToListAsync(stoppingToken);
// Comparamos las dos listas para encontrar los que realmente son nuevos
var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList();
if (!nuevosTelegramasIds.Any())
{
_logger.LogInformation("No hay telegramas nuevos para la sección {seccionId}.", seccion.SeccionId);
continue;
}
_logger.LogInformation("Se encontraron {count} telegramas nuevos en la sección {seccionId}. Descargando...", nuevosTelegramasIds.Count, seccion.SeccionId);
foreach (var mesaId in nuevosTelegramasIds)
{
if (stoppingToken.IsCancellationRequested) break;
var telegramaFile = await _apiService.GetTelegramaFileAsync(_authToken, mesaId);
if (telegramaFile != null)
{
var nuevoTelegrama = new Telegrama
{
Id = telegramaFile.NombreArchivo,
AmbitoGeograficoId = seccion.Id, // Lo asociamos a la sección por simplicidad
ContenidoBase64 = telegramaFile.Imagen,
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
};
// Como estamos en un DbContext fresco, AddAsync no dará conflicto
await dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
}
}
// Guardamos los cambios al final de cada sección procesada
await dbContext.SaveChangesAsync(stoppingToken);
}
}
_logger.LogInformation("Sondeo de Telegramas completado.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error en el sondeo de Telegramas.");
}
}
private async Task SondearResumenProvincialAsync(CancellationToken stoppingToken)
{
try
{
await ObtenerTokenSiEsNecesario(stoppingToken);
if (string.IsNullOrEmpty(_authToken)) return;
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
if (provincia == null) return;
var resumen = await _apiService.GetResumenAsync(_authToken, provincia.DistritoId!);
if (resumen?.ValoresTotalizadosPositivos is { Count: > 0 })
{
// Estrategia: Reemplazo completo
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken);
foreach (var voto in resumen.ValoresTotalizadosPositivos)
{
await dbContext.ResumenesVotos.AddAsync(new ResumenVoto
{
AmbitoGeograficoId = provincia.Id,
AgrupacionPoliticaId = voto.IdAgrupacion,
Votos = voto.Votos,
VotosPorcentaje = voto.VotosPorcentaje
}, stoppingToken);
}
await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("Sondeo de Resumen Provincial completado.");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error en el sondeo de Resumen Provincial.");
}
}
private async Task SondearEstadoRecuentoGeneralAsync(CancellationToken stoppingToken)
{
try
{
await ObtenerTokenSiEsNecesario(stoppingToken);
if (string.IsNullOrEmpty(_authToken)) return;
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
if (provincia == null) return;
var estadoDto = await _apiService.GetEstadoRecuentoGeneralAsync(_authToken, provincia.DistritoId!);
if (estadoDto != null)
{
// Estrategia: Upsert
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(provincia.Id);
if (registroDb == null)
{
registroDb = new EstadoRecuentoGeneral { AmbitoGeograficoId = provincia.Id };
dbContext.EstadosRecuentosGenerales.Add(registroDb);
}
registroDb.MesasEsperadas = estadoDto.MesasEsperadas;
registroDb.MesasTotalizadas = estadoDto.MesasTotalizadas;
registroDb.MesasTotalizadasPorcentaje = estadoDto.MesasTotalizadasPorcentaje;
registroDb.CantidadElectores = estadoDto.CantidadElectores;
registroDb.CantidadVotantes = estadoDto.CantidadVotantes;
registroDb.ParticipacionPorcentaje = estadoDto.ParticipacionPorcentaje;
await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("Sondeo de Estado Recuento General completado.");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error en el sondeo de Estado Recuento General.");
}
}
}

View File

@@ -12,7 +12,11 @@
"Elecciones.Infrastructure": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "9.0.8",
"Microsoft.Extensions.Hosting": "9.0.5",
"Microsoft.Extensions.Http": "9.0.8"
"Microsoft.Extensions.Http": "9.0.8",
"Serilog.Extensions.Hosting": "9.0.0",
"Serilog.Settings.Configuration": "9.0.0",
"Serilog.Sinks.Console": "6.0.0",
"Serilog.Sinks.File": "7.0.0"
},
"runtime": {
"Elecciones.Worker.dll": {}
@@ -323,6 +327,14 @@
}
}
},
"Microsoft.Extensions.DependencyModel/9.0.0": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.DependencyModel.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52809"
}
}
},
"Microsoft.Extensions.Diagnostics/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration": "9.0.8",
@@ -698,6 +710,76 @@
}
}
},
"Serilog/4.2.0": {
"runtime": {
"lib/net9.0/Serilog.dll": {
"assemblyVersion": "4.2.0.0",
"fileVersion": "4.2.0.0"
}
}
},
"Serilog.Extensions.Hosting/9.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Hosting.Abstractions": "9.0.5",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Serilog": "4.2.0",
"Serilog.Extensions.Logging": "9.0.0"
},
"runtime": {
"lib/net9.0/Serilog.Extensions.Hosting.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Extensions.Logging/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Logging": "9.0.8",
"Serilog": "4.2.0"
},
"runtime": {
"lib/net9.0/Serilog.Extensions.Logging.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Settings.Configuration/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
"Microsoft.Extensions.DependencyModel": "9.0.0",
"Serilog": "4.2.0"
},
"runtime": {
"lib/net9.0/Serilog.Settings.Configuration.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Serilog.Sinks.Console/6.0.0": {
"dependencies": {
"Serilog": "4.2.0"
},
"runtime": {
"lib/net8.0/Serilog.Sinks.Console.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.0.0"
}
}
},
"Serilog.Sinks.File/7.0.0": {
"dependencies": {
"Serilog": "4.2.0"
},
"runtime": {
"lib/net9.0/Serilog.Sinks.File.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.0.0"
}
}
},
"System.ClientModel/1.0.0": {
"dependencies": {
"System.Memory.Data": "1.0.2",
@@ -1115,6 +1197,13 @@
"path": "microsoft.extensions.dependencyinjection.abstractions/9.0.8",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.DependencyModel/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-saxr2XzwgDU77LaQfYFXmddEDRUKHF4DaGMZkNB3qjdVSZlax3//dGJagJkKrGMIPNZs2jVFXITyCCR6UHJNdA==",
"path": "microsoft.extensions.dependencymodel/9.0.0",
"hashPath": "microsoft.extensions.dependencymodel.9.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Diagnostics/9.0.8": {
"type": "package",
"serviceable": true,
@@ -1325,6 +1414,48 @@
"path": "microsoft.win32.systemevents/6.0.0",
"hashPath": "microsoft.win32.systemevents.6.0.0.nupkg.sha512"
},
"Serilog/4.2.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-gmoWVOvKgbME8TYR+gwMf7osROiWAURterc6Rt2dQyX7wtjZYpqFiA/pY6ztjGQKKV62GGCyOcmtP1UKMHgSmA==",
"path": "serilog/4.2.0",
"hashPath": "serilog.4.2.0.nupkg.sha512"
},
"Serilog.Extensions.Hosting/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-u2TRxuxbjvTAldQn7uaAwePkWxTHIqlgjelekBtilAGL5sYyF3+65NWctN4UrwwGLsDC7c3Vz3HnOlu+PcoxXg==",
"path": "serilog.extensions.hosting/9.0.0",
"hashPath": "serilog.extensions.hosting.9.0.0.nupkg.sha512"
},
"Serilog.Extensions.Logging/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==",
"path": "serilog.extensions.logging/9.0.0",
"hashPath": "serilog.extensions.logging.9.0.0.nupkg.sha512"
},
"Serilog.Settings.Configuration/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-4/Et4Cqwa+F88l5SeFeNZ4c4Z6dEAIKbu3MaQb2Zz9F/g27T5a3wvfMcmCOaAiACjfUb4A6wrlTVfyYUZk3RRQ==",
"path": "serilog.settings.configuration/9.0.0",
"hashPath": "serilog.settings.configuration.9.0.0.nupkg.sha512"
},
"Serilog.Sinks.Console/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==",
"path": "serilog.sinks.console/6.0.0",
"hashPath": "serilog.sinks.console.6.0.0.nupkg.sha512"
},
"Serilog.Sinks.File/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fKL7mXv7qaiNBUC71ssvn/dU0k9t0o45+qm2XgKAlSt19xF+ijjxyA3R6HmCgfKEKwfcfkwWjayuQtRueZFkYw==",
"path": "serilog.sinks.file/7.0.0",
"hashPath": "serilog.sinks.file.7.0.0.nupkg.sha512"
},
"System.ClientModel/1.0.0": {
"type": "package",
"serviceable": true,

View File

@@ -4,10 +4,5 @@
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ElectoralApi": {
"BaseUrl": "https://api.eleccionesbonaerenses.gba.gob.ar",
"Username": "30500094156@elecciones2025.onmicrosoft.com",
"Password": "PTP847elec"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@@ -0,0 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ElectoralApi": {
"BaseUrl": "https://api.eleccionesbonaerenses.gba.gob.ar",
"Username": "30500094156@elecciones2025.onmicrosoft.com",
"Password": "PTP847elec"
},
"ConnectionStrings": {
"DefaultConnection": "Server=TECNICA3;Database=Elecciones2025;User Id=apielecciones2025;Password=PTP847Elecciones2025;Encrypt=False;MultipleActiveResultSets=True;TrustServerCertificate=True;"
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b90baadeedb870b5b1c9eeeb7022a0d211b61bec")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -96,3 +96,11 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Debug\net9.0\Eleccio
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Debug\net9.0\Elecciones.Worker.genruntimeconfig.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Debug\net9.0\ref\Elecciones.Worker.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Microsoft.Extensions.Http.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\buenos-aires-municipios.geojson
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Microsoft.Extensions.DependencyModel.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Serilog.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Serilog.Extensions.Hosting.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Serilog.Extensions.Logging.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Serilog.Settings.Configuration.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Serilog.Sinks.Console.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Debug\net9.0\Serilog.Sinks.File.dll

View File

@@ -289,6 +289,22 @@
"Microsoft.Extensions.Http": {
"target": "Package",
"version": "[9.0.8, )"
},
"Serilog.Extensions.Hosting": {
"target": "Package",
"version": "[9.0.0, )"
},
"Serilog.Settings.Configuration": {
"target": "Package",
"version": "[9.0.0, )"
},
"Serilog.Sinks.Console": {
"target": "Package",
"version": "[6.0.0, )"
},
"Serilog.Sinks.File": {
"target": "Package",
"version": "[7.0.0, )"
}
},
"imports": [

View File

@@ -2,8 +2,8 @@
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)system.text.json\9.0.8\buildTransitive\net8.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\9.0.8\buildTransitive\net8.0\System.Text.Json.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.usersecrets\9.0.5\buildTransitive\net8.0\Microsoft.Extensions.Configuration.UserSecrets.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.usersecrets\9.0.5\buildTransitive\net8.0\Microsoft.Extensions.Configuration.UserSecrets.targets')" />
</ImportGroup>

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]

View File

@@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute("dotnet-Elecciones.Worker-b1c6e5c0-7ebf-4eaf-af95-9386d6883c03")]
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+39b1e9707275ed59ac4a7d32e26b951186a346bb")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generado por la clase WriteCodeFragment de MSBuild.

View File

@@ -0,0 +1,15 @@
is_global = true
build_property.TargetFramework = net9.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Elecciones.Worker
build_property.ProjectDir = E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,12 @@
// <auto-generated/>
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@@ -0,0 +1,106 @@
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\buenos-aires-municipios.geojson
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\appsettings.Development.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\appsettings.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Worker.exe
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Worker.deps.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Worker.runtimeconfig.json
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Worker.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Worker.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Azure.Core.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Azure.Identity.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Bcl.AsyncInterfaces.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Data.SqlClient.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.EntityFrameworkCore.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.EntityFrameworkCore.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.EntityFrameworkCore.Relational.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.EntityFrameworkCore.SqlServer.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Caching.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Caching.Memory.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.Binder.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.CommandLine.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.EnvironmentVariables.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.FileExtensions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.Json.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Configuration.UserSecrets.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.DependencyInjection.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.DependencyModel.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Diagnostics.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Diagnostics.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.FileProviders.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.FileProviders.Physical.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.FileSystemGlobbing.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Hosting.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Hosting.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Http.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.Configuration.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.Console.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.Debug.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.EventLog.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Logging.EventSource.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Options.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Options.ConfigurationExtensions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Extensions.Primitives.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Identity.Client.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Identity.Client.Extensions.Msal.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.IdentityModel.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.IdentityModel.JsonWebTokens.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.IdentityModel.Logging.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.IdentityModel.Protocols.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.IdentityModel.Tokens.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.SqlServer.Server.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Microsoft.Win32.SystemEvents.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Serilog.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Serilog.Extensions.Hosting.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Serilog.Extensions.Logging.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Serilog.Settings.Configuration.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Serilog.Sinks.Console.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Serilog.Sinks.File.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.ClientModel.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Configuration.ConfigurationManager.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Diagnostics.EventLog.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Drawing.Common.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Formats.Asn1.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.IdentityModel.Tokens.Jwt.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Memory.Data.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Runtime.Caching.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Security.Cryptography.ProtectedData.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Security.Permissions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Text.Json.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\System.Windows.Extensions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\unix\lib\net6.0\Microsoft.Data.SqlClient.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net6.0\Microsoft.Data.SqlClient.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win-arm\native\Microsoft.Data.SqlClient.SNI.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win-arm64\native\Microsoft.Data.SqlClient.SNI.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win-x64\native\Microsoft.Data.SqlClient.SNI.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win-x86\native\Microsoft.Data.SqlClient.SNI.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net6.0\Microsoft.Win32.SystemEvents.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net9.0\System.Diagnostics.EventLog.Messages.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net9.0\System.Diagnostics.EventLog.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\unix\lib\net6.0\System.Drawing.Common.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net6.0\System.Drawing.Common.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net6.0\System.Runtime.Caching.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net6.0\System.Security.Cryptography.ProtectedData.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\runtimes\win\lib\net6.0\System.Windows.Extensions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Core.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Database.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Infrastructure.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Infrastructure.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Database.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\bin\Release\net9.0\Elecciones.Core.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.csproj.AssemblyReference.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.GeneratedMSBuildEditorConfig.editorconfig
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.AssemblyInfoInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.AssemblyInfo.cs
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.csproj.CoreCompileInputs.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Eleccion.0707F6F5.Up2Date
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\refint\Elecciones.Worker.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\Elecciones.Worker.genruntimeconfig.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Worker\obj\Release\net9.0\ref\Elecciones.Worker.dll

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetEFProjectMetadata">
<MSBuild Condition=" '$(TargetFramework)' == '' "
Projects="$(MSBuildProjectFile)"
Targets="GetEFProjectMetadata"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);EFProjectMetadataFile=$(EFProjectMetadataFile)" />
<ItemGroup Condition=" '$(TargetFramework)' != '' ">
<EFProjectMetadata Include="AssemblyName: $(AssemblyName)" />
<EFProjectMetadata Include="Language: $(Language)" />
<EFProjectMetadata Include="OutputPath: $(OutputPath)" />
<EFProjectMetadata Include="Platform: $(Platform)" />
<EFProjectMetadata Include="PlatformTarget: $(PlatformTarget)" />
<EFProjectMetadata Include="ProjectAssetsFile: $(ProjectAssetsFile)" />
<EFProjectMetadata Include="ProjectDir: $(ProjectDir)" />
<EFProjectMetadata Include="RootNamespace: $(RootNamespace)" />
<EFProjectMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
<EFProjectMetadata Include="TargetFileName: $(TargetFileName)" />
<EFProjectMetadata Include="TargetFrameworkMoniker: $(TargetFrameworkMoniker)" />
<EFProjectMetadata Include="Nullable: $(Nullable)" />
<EFProjectMetadata Include="TargetFramework: $(TargetFramework)" />
<EFProjectMetadata Include="TargetPlatformIdentifier: $(TargetPlatformIdentifier)" />
</ItemGroup>
<WriteLinesToFile Condition=" '$(TargetFramework)' != '' "
File="$(EFProjectMetadataFile)"
Lines="@(EFProjectMetadata)" />
</Target>
</Project>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetEFProjectMetadata">
<MSBuild Condition=" '$(TargetFramework)' == '' "
Projects="$(MSBuildProjectFile)"
Targets="GetEFProjectMetadata"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);EFProjectMetadataFile=$(EFProjectMetadataFile)" />
<ItemGroup Condition=" '$(TargetFramework)' != '' ">
<EFProjectMetadata Include="AssemblyName: $(AssemblyName)" />
<EFProjectMetadata Include="Language: $(Language)" />
<EFProjectMetadata Include="OutputPath: $(OutputPath)" />
<EFProjectMetadata Include="Platform: $(Platform)" />
<EFProjectMetadata Include="PlatformTarget: $(PlatformTarget)" />
<EFProjectMetadata Include="ProjectAssetsFile: $(ProjectAssetsFile)" />
<EFProjectMetadata Include="ProjectDir: $(ProjectDir)" />
<EFProjectMetadata Include="RootNamespace: $(RootNamespace)" />
<EFProjectMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
<EFProjectMetadata Include="TargetFileName: $(TargetFileName)" />
<EFProjectMetadata Include="TargetFrameworkMoniker: $(TargetFrameworkMoniker)" />
<EFProjectMetadata Include="Nullable: $(Nullable)" />
<EFProjectMetadata Include="TargetFramework: $(TargetFramework)" />
<EFProjectMetadata Include="TargetPlatformIdentifier: $(TargetPlatformIdentifier)" />
</ItemGroup>
<WriteLinesToFile Condition=" '$(TargetFramework)' != '' "
File="$(EFProjectMetadataFile)"
Lines="@(EFProjectMetadata)" />
</Target>
</Project>

78
reconcile_ids.js Normal file
View File

@@ -0,0 +1,78 @@
// reconcile_ids.js
const fs = require('fs');
const path = require('path');
const http = require('http');
// --- CONFIGURACIÓN ---
const API_URL = 'http://localhost:5217/api/catalogos/municipios'; // ¡Asegúrate que tu API esté corriendo en este puerto!
const GEOJSON_PATH = path.join(__dirname, 'Elecciones-Web', 'frontend', 'public', 'buenos-aires-municipios.geojson');
const OUTPUT_PATH = path.join(__dirname, 'Elecciones-Web', 'frontend', 'public', 'municipioIdMap.json');
const normalizeString = (str) => {
if (!str) return '';
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
};
async function reconcile() {
console.log('Iniciando el script de reconciliación de IDs...');
try {
const apiData = await new Promise((resolve, reject) => {
http.get(API_URL, res => {
const { statusCode } = res;
if (statusCode !== 200) {
reject(new Error(`La petición a la API falló con el código de estado: ${statusCode}`));
res.resume();
return;
}
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(JSON.parse(data)));
}).on('error', reject);
});
console.log(`Se obtuvieron ${apiData.length} municipios desde la API.`);
const apiNameMap = new Map(apiData.map(m => [normalizeString(m.nombre), m.id]));
const geoJsonRaw = fs.readFileSync(GEOJSON_PATH, 'utf8');
const geoJsonData = JSON.parse(geoJsonRaw);
console.log(`Se leyeron ${geoJsonData.features.length} features desde el GeoJSON.`);
const finalIdMap = {};
let matches = 0;
const notFound = [];
// --- CAMBIO CLAVE: YA NO NECESITAMOS ALIAS MANUALES ---
for (const feature of geoJsonData.features) {
const geoJsonId = feature.properties.cca;
const nombreGeoJson = normalizeString(feature.properties.nam);
if (apiNameMap.has(nombreGeoJson)) {
const apiId = apiNameMap.get(nombreGeoJson);
finalIdMap[geoJsonId] = apiId;
matches++;
} else {
notFound.push(feature.properties.nam);
}
}
console.log(`\n--- RESULTADOS ---`);
console.log(`Coincidencias encontradas: ${matches} de ${geoJsonData.features.length}`);
if (notFound.length > 0) {
console.warn(`\nNo se encontró coincidencia para los siguientes ${notFound.length} municipios del GeoJSON:`);
console.warn(notFound.join(', '));
}
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(finalIdMap, null, 2));
console.log(`\n¡Éxito! El mapa de IDs se ha guardado en: ${OUTPUT_PATH}`);
} catch (error) {
console.error('\nOcurrió un error:', error.message);
if(error.code === 'ECONNREFUSED') {
console.error('Asegúrate de que tu servicio de API esté corriendo en la URL especificada.');
}
}
}
reconcile();