Proyecto creado
This commit is contained in:
180
.gitignore
vendored
Normal file
180
.gitignore
vendored
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# ---> Python
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# UV
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
#uv.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||||
|
.pdm.toml
|
||||||
|
.pdm-python
|
||||||
|
.pdm-build/
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
# Ruff stuff:
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# PyPI configuration file
|
||||||
|
.pypirc
|
||||||
|
|
||||||
|
#archivos para este proyecto
|
||||||
|
*.pdf
|
||||||
|
*.csv
|
||||||
|
*.json
|
||||||
18
LICENSE
Normal file
18
LICENSE
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Drk0027
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
|
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||||
|
portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||||
|
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||||
|
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
99
README.md
Normal file
99
README.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# Consumo Eléctrico - Extractor y Analizador
|
||||||
|
|
||||||
|
Este proyecto permite **extraer automáticamente datos de consumo eléctrico** desde planillas de luz en formato PDF (texto digital), organizarlos en tablas y exportarlos a **CSV y JSON** para análisis y visualización.
|
||||||
|
|
||||||
|
## Características
|
||||||
|
- Extrae el **Valor total** desde la fila `VALOR TOTAL` en la columna `Valor total`.
|
||||||
|
- Obtiene también la **fecha** de cada planilla.
|
||||||
|
- Exporta resultados a:
|
||||||
|
- `consumo.csv` → tabla lista para abrir en Excel o LibreOffice.
|
||||||
|
- `consumo.json` → formato estructurado para integraciones o APIs.
|
||||||
|
- Genera gráficos comparativos:
|
||||||
|
- Evolución mensual del consumo.
|
||||||
|
- Comparación entre el último año y el anterior.
|
||||||
|
- Etiquetas con valores exactos en cada punto. (mas o menos)
|
||||||
|
|
||||||
|
## Ejemplo de salida
|
||||||
|
|
||||||
|
**CSV (`consumo.csv`):**
|
||||||
|
```csv
|
||||||
|
archivo,Fecha,KW Consumidos,Valor total,Valor SE y AP,Valor Basura
|
||||||
|
enero-2024.pdf,10-01-2024,679.00,94.98,86.37,6.31
|
||||||
|
febrero-2024.pdf,08-02-2024,619.00,86.62,78.58,5.74
|
||||||
|
marzo-2024.pdf,11-03-2024,604.00,84.53,76.63,5.6
|
||||||
|
```
|
||||||
|
|
||||||
|
**JSON (`consumo.json`):**
|
||||||
|
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"archivo":"enero-2024.pdf",
|
||||||
|
"Fecha":"10-01-2024",
|
||||||
|
"KW Consumidos":"679.00",
|
||||||
|
"Valor total":94.98,
|
||||||
|
"Valor SE y AP":86.37,
|
||||||
|
"Valor Basura":6.31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"archivo":"febrero-2024.pdf",
|
||||||
|
"Fecha":"08-02-2024",
|
||||||
|
"KW Consumidos":"619.00",
|
||||||
|
"Valor total":86.62,
|
||||||
|
"Valor SE y AP":78.58,
|
||||||
|
"Valor Basura":5.74
|
||||||
|
},
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gráfico comparativo:**
|
||||||
|
- Línea azul → último año.
|
||||||
|
- Línea roja → año anterior.
|
||||||
|
- Etiquetas con valores de consumo en cada mes.
|
||||||
|
|
||||||
|
## Instalación
|
||||||
|
|
||||||
|
1. Clona este repositorio:
|
||||||
|
```bash
|
||||||
|
git clone https://git.interlan.ec/Drk0027/consumo-electrico.git
|
||||||
|
cd consumo-electrico
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Instala dependencias:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Dependencias principales:
|
||||||
|
- `pdfplumber`
|
||||||
|
- `pandas`
|
||||||
|
- `matplotlib`
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
|
||||||
|
1. Coloca tus archivos PDF en la carpeta con nombres en formato `nombremes-año.pdf`.
|
||||||
|
2. Edita `main.py` en la fila 7, con los nombres de tus archivos pdf.
|
||||||
|
3. Ejecuta el script principal:
|
||||||
|
```bash
|
||||||
|
python extraer_consumo.py
|
||||||
|
```
|
||||||
|
4. Revisa los archivos generados:
|
||||||
|
- `consumo.csv`
|
||||||
|
- `consumo.json`
|
||||||
|
- Gráficos en pantalla o exportados como imágenes.
|
||||||
|
|
||||||
|
## 💡 Ideas de mejora
|
||||||
|
- Extracción de **costo total** y otros campos (tarifa, número de medidor).
|
||||||
|
- ¿Alguna forma de automatizacion para descargar regularmente las facturas que llegan al correo?
|
||||||
|
- Mas Flexibilidad para no tener que agregar los archivos de forma manual.
|
||||||
|
|
||||||
|
## 📬 Sugerencias y contacto
|
||||||
|
Me encantaría recibir tus ideas y mejoras.
|
||||||
|
Puedes escribirme a:
|
||||||
|
|
||||||
|
- 📧 **Correo:** `drk0027@interlan.ec`
|
||||||
|
- 📢 **Canal de Telegram:** [t.me/drk0072](https://t.me/drk0072)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
¡Gracias por usar este proyecto! Tu retroalimentación ayuda a que siga creciendo.
|
||||||
92
main.py
Normal file
92
main.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import pdfplumber
|
||||||
|
import re
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
archivos = ["enero-2024.pdf", "febrero-2024.pdf", "marzo-2024.pdf","abril-2024.pdf","junio-2024.pdf","julio-2024.pdf","agosto-2024.pdf","septiembre-2024.pdf","octubre-2024.pdf","noviembre-2024.pdf","diciembre-2024.pdf","enero-2025.pdf", "febrero-2025.pdf", "marzo-2025.pdf","abril-2025.pdf","junio-2025.pdf","julio-2025.pdf","agosto-2025.pdf","septiembre-2025.pdf","octubre-2025.pdf","noviembre-2025.pdf","diciembre-2025.pdf"]
|
||||||
|
|
||||||
|
datos = []
|
||||||
|
|
||||||
|
for archivo in archivos:
|
||||||
|
with pdfplumber.open(archivo) as pdf:
|
||||||
|
texto = " ".join([p.extract_text() for p in pdf.pages if p.extract_text()])
|
||||||
|
#print(texto)
|
||||||
|
|
||||||
|
# Patrones
|
||||||
|
fecha = re.search(r"Fecha hasta *?(\d{2}-\d{2}-\d{4})", texto)
|
||||||
|
consumo = re.search(r"Consumo.*?(\d+)\s*kWh", texto)
|
||||||
|
total = re.search(r"VALOR TOTAL.*?\$?([\d,.]+)", texto)
|
||||||
|
val_imp = re.search(r"TOTAL SE Y AP \(1\).*?\$?([\d,.]+)", texto)
|
||||||
|
val_basura = re.search(r"TOTAL RECOLECCIÓN BASURA \(5\).*?\$?([\d,.]+)", texto)
|
||||||
|
|
||||||
|
#print(fecha)
|
||||||
|
for pagina in pdf.pages:
|
||||||
|
tablas = pagina.extract_tables()
|
||||||
|
for tabla in tablas:
|
||||||
|
df_tabla = pd.DataFrame(tabla[1:], columns=tabla[0])
|
||||||
|
|
||||||
|
# Buscar la fila específica
|
||||||
|
fila = df_tabla[df_tabla.iloc[:,0].str.contains("Energía Facturada", case=False, na=False)]
|
||||||
|
if not fila.empty:
|
||||||
|
consumo = fila["Consumo Total"].values[0] # nombre exacto de la columna
|
||||||
|
datos.append({
|
||||||
|
"archivo": archivo,
|
||||||
|
"Fecha" : fecha.group(1) if fecha else None,
|
||||||
|
"KW Consumidos": consumo,
|
||||||
|
"Valor total": float(total.group(1).replace(",", "")) if total else None,
|
||||||
|
"Valor SE y AP": float(val_imp.group(1).replace(",", "")) if val_imp else None,
|
||||||
|
"Valor Basura" : float(val_basura.group(1).replace(",", "")) if val_basura else None
|
||||||
|
})
|
||||||
|
|
||||||
|
df = pd.DataFrame(datos)
|
||||||
|
print(df)
|
||||||
|
|
||||||
|
# Exportar a CSV
|
||||||
|
df.to_csv("consumo.csv", index=False)
|
||||||
|
|
||||||
|
# Exportar a JSON
|
||||||
|
df.to_json("consumo.json", orient="records", indent=4)
|
||||||
|
|
||||||
|
print("Datos exportados a consumo.csv y consumo.json")
|
||||||
|
|
||||||
|
# aqui grafica los resultados
|
||||||
|
|
||||||
|
# Cargar datos
|
||||||
|
df = pd.read_csv("consumo.csv")
|
||||||
|
|
||||||
|
# Convertir fechas
|
||||||
|
df["Fecha"] = pd.to_datetime(df["Fecha"], dayfirst=True)
|
||||||
|
df["anio"] = df["Fecha"].dt.year
|
||||||
|
df["mes"] = df["Fecha"].dt.month
|
||||||
|
|
||||||
|
# Identificar último año y anterior
|
||||||
|
ultimo_anio = df["anio"].max()
|
||||||
|
anterior_anio = ultimo_anio - 1
|
||||||
|
|
||||||
|
# Filtrar datos
|
||||||
|
df_ultimo = df[df["anio"] == ultimo_anio]
|
||||||
|
df_anterior = df[df["anio"] == anterior_anio]
|
||||||
|
|
||||||
|
# Graficar comparación
|
||||||
|
plt.figure(figsize=(10,6))
|
||||||
|
plt.plot(df_anterior["mes"], df_anterior["KW Consumidos"], marker="o", label=f"Año {anterior_anio}", color="red")
|
||||||
|
plt.plot(df_ultimo["mes"], df_ultimo["KW Consumidos"], marker="o", label=f"Año {ultimo_anio}", color="blue")
|
||||||
|
|
||||||
|
|
||||||
|
# etiquetas a los valores
|
||||||
|
|
||||||
|
for x, y in zip(df_anterior["mes"], df_anterior["Valor total"]):
|
||||||
|
plt.text(x, y+5, str(y), color="red", ha="center", fontsize=8)
|
||||||
|
|
||||||
|
for x, y in zip(df_ultimo["mes"], df_ultimo["Valor total"]):
|
||||||
|
plt.text(x, y+5, str(y), color="blue", ha="center", fontsize=8)
|
||||||
|
|
||||||
|
plt.title("Comparación de consumo eléctrico entre dos años")
|
||||||
|
plt.xlabel("Mes")
|
||||||
|
plt.ylabel("Consumo Total (kWh)")
|
||||||
|
plt.xticks(range(1,13))
|
||||||
|
plt.legend()
|
||||||
|
plt.grid(True)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
||||||
11
requirements.txt
Normal file
11
requirements.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
matplotlib==3.9.2
|
||||||
|
multidict==6.1.0
|
||||||
|
numpy==2.1.2
|
||||||
|
pandas==2.2.3
|
||||||
|
pdfminer.six==20251107
|
||||||
|
pdfplumber==0.11.8
|
||||||
|
pillow==11.0.0
|
||||||
|
plotly==5.24.1
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
pytz==2024.2
|
||||||
|
tzdata==2024.2
|
||||||
Reference in New Issue
Block a user