parent
1a32b70a2f
commit
dcffd3658c
@ -0,0 +1,36 @@
|
||||
# Gebruik een officiële Python-image als basis
|
||||
FROM python:3.9-slim
|
||||
|
||||
RUN apt-get update && apt-get -y install cron nano procps libmariadb-dev
|
||||
# Werkdirectory instellen
|
||||
WORKDIR /app
|
||||
|
||||
# Kopieer je Python-bestanden naar de container
|
||||
COPY . /app
|
||||
|
||||
# Kopieer de requirements.txt naar de container
|
||||
COPY requirements.txt .
|
||||
# Kopieer de requirements.txt naar de container
|
||||
COPY .env .
|
||||
|
||||
# Installeer de vereiste Python dependencies
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Installeer cron en zorg ervoor dat de cron-demon actief is
|
||||
RUN apt-get update && apt-get install -y cron
|
||||
|
||||
# Voeg het cron-script toe
|
||||
COPY cronfile.energie /etc/cron.d/cronfile.energie
|
||||
# Zorg ervoor dat het cron-script uitvoerbaar is
|
||||
RUN chmod 0644 /etc/cron.d/cronfile.energie
|
||||
|
||||
# Voeg een cronjob toe die de Python-script dagelijks uitvoert
|
||||
RUN crontab /etc/cron.d/cronfile.energie
|
||||
|
||||
|
||||
# Voeg start.sh toe
|
||||
COPY start.sh /start.sh
|
||||
RUN chmod +x /start.sh
|
||||
|
||||
# Gebruik start.sh om zowel cron als currentprice.py te starten
|
||||
CMD ["/start.sh"]
|
||||
@ -0,0 +1,18 @@
|
||||
import mariadb
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Configuratie laden uit .env-bestand
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def get_db_connection():
|
||||
"""Maakt een verbinding met de MariaDB-database en retourneert de cursor en verbinding."""
|
||||
conn = mariadb.connect(
|
||||
host=os.getenv("DB_HOST"),
|
||||
user=os.getenv("DB_USER"),
|
||||
password=os.getenv("DB_PASSWORD"),
|
||||
database=os.getenv("DB_NAME"),
|
||||
port=int(os.getenv("DB_PORT"))
|
||||
)
|
||||
return conn, conn.cursor()
|
||||
@ -0,0 +1,15 @@
|
||||
# docker compose -f energyprices_docker-compose.yml up -d --build
|
||||
|
||||
version: "3.3"
|
||||
services:
|
||||
delflanddev:
|
||||
restart: always
|
||||
container_name: delflanddev
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Amsterdam
|
||||
build:
|
||||
dockerfile: ./Dockerfile
|
||||
env_file:
|
||||
- .env
|
||||
@ -0,0 +1,226 @@
|
||||
import os
|
||||
import pandas as pd
|
||||
from flask import Flask, request, render_template, redirect, url_for, flash
|
||||
from werkzeug.utils import secure_filename
|
||||
from dotenv import load_dotenv
|
||||
from database import get_db_connection
|
||||
from flask import Response
|
||||
import io
|
||||
from datetime import datetime
|
||||
|
||||
# Configuratie laden
|
||||
env_path = "./.env"
|
||||
load_dotenv(env_path)
|
||||
|
||||
UPLOAD_FOLDER = os.getenv("UPLOAD_FOLDER")
|
||||
ALLOWED_EXTENSIONS = {"xlsx"}
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
|
||||
app.secret_key = "supersecretkey"
|
||||
|
||||
|
||||
# Controleer of bestand toegestaan is
|
||||
def allowed_file(filename):
|
||||
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
|
||||
# Route voor de uploadpagina
|
||||
@app.route("/", methods=["GET", "POST"])
|
||||
def upload_file():
|
||||
if request.method == "POST":
|
||||
if "file" not in request.files:
|
||||
flash("Geen bestand geselecteerd", "danger")
|
||||
return redirect(request.url)
|
||||
|
||||
file = request.files["file"]
|
||||
if file.filename == "":
|
||||
flash("Geen bestand geselecteerd", "danger")
|
||||
return redirect(request.url)
|
||||
|
||||
if file and allowed_file(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
filepath = os.path.join(app.config["UPLOAD_FOLDER"], filename)
|
||||
file.save(filepath)
|
||||
flash("Bestand geüpload en verwerkt! <a href='" +
|
||||
url_for('show_result') + "'>Bekijk de resultaten</a>", "success")
|
||||
process_excel(filepath)
|
||||
return redirect(url_for("upload_file"))
|
||||
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route("/extrapolate", methods=["GET", "POST"])
|
||||
def show_result():
|
||||
x = request.form.get("x", 1, type=int)
|
||||
result = getextrapolation(x)
|
||||
|
||||
# Geef de resultaten door aan het HTML-template
|
||||
return render_template("resultaat.html", result=result, x=x)
|
||||
|
||||
|
||||
@app.route("/download", methods=["GET"])
|
||||
def download_excel():
|
||||
x = request.args.get("x", 1, type=int)
|
||||
result = getextrapolation(x)
|
||||
|
||||
df = pd.DataFrame(result)
|
||||
|
||||
# Excel-bestand in-memory genereren
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
df.to_excel(writer, index=False, sheet_name=f"Extrapolatie_{x}jaar")
|
||||
|
||||
output.seek(0)
|
||||
|
||||
# Dynamische bestandsnaam
|
||||
filename = f"extrapolatie_{x}jaar.xlsx"
|
||||
|
||||
return Response(
|
||||
output,
|
||||
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f"attachment; filename={filename}"}
|
||||
)
|
||||
|
||||
|
||||
def getextrapolation(extrapolateYears):
|
||||
conn, cursor = get_db_connection()
|
||||
|
||||
# Haal de originele data op zonder extrapolatie
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
Object_code,
|
||||
Object_omschrijving,
|
||||
Afdeling_object,
|
||||
PO_Code,
|
||||
Omschrijving_PO,
|
||||
Vervaldatum,
|
||||
Frequentie,
|
||||
UOM,
|
||||
PO_schema_Niet_gebruikt,
|
||||
Cluster,
|
||||
Locatie,
|
||||
Locatie_omschrijving,
|
||||
Klasse_object,
|
||||
Categorie
|
||||
FROM onderhoud
|
||||
""")
|
||||
|
||||
# Haal de kolomnamen op
|
||||
columns = [desc[0] for desc in cursor.description]
|
||||
|
||||
# Haal de ruwe data op
|
||||
rows = cursor.fetchall()
|
||||
|
||||
# Sluit de cursor en de verbinding
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
raw_data = [dict(zip(columns, row)) for row in rows]
|
||||
|
||||
# Huidige datum als pd.Timestamp (zonder tijd)
|
||||
today = pd.Timestamp.today().normalize()
|
||||
|
||||
result = []
|
||||
for row in raw_data:
|
||||
vervaldatum = row.get("Vervaldatum")
|
||||
|
||||
# Converteer de Vervaldatum correct naar pd.Timestamp (of None bij fouten)
|
||||
vervaldatum = pd.to_datetime(vervaldatum, errors="coerce")
|
||||
|
||||
# Controleer of de datum geldig is en in het verleden ligt
|
||||
if pd.notna(vervaldatum) and vervaldatum < today:
|
||||
vervaldatum = today # Zet op vandaag
|
||||
|
||||
for i in range(extrapolateYears + 1):
|
||||
new_row = row.copy()
|
||||
|
||||
if pd.notna(vervaldatum): # Controleer of het een geldige datum is
|
||||
new_row["Vervaldatum"] = (
|
||||
vervaldatum + pd.DateOffset(years=i)).strftime("%Y-%m-%d")
|
||||
else:
|
||||
# Indien geen geldige datum, zet als None
|
||||
new_row["Vervaldatum"] = None
|
||||
|
||||
result.append(new_row)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def process_exceld(filepath):
|
||||
df = pd.read_excel(filepath, engine="openpyxl")
|
||||
print(df.columns)
|
||||
df.columns = df.columns.str.strip()
|
||||
# for _, row in df.iterrows():
|
||||
# print(row["Object_omschrijving"])
|
||||
|
||||
|
||||
# Verwerk het Excel-bestand en sla op in MariaDB
|
||||
def process_excel(filepath):
|
||||
df = pd.read_excel(filepath, engine="openpyxl")
|
||||
df.columns = df.columns.str.strip()
|
||||
# Databaseverbinding maken
|
||||
conn, cursor = get_db_connection()
|
||||
|
||||
# Zorg dat de tabel bestaat
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS onderhoud (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
Object_code VARCHAR(50),
|
||||
Object_omschrijving TEXT,
|
||||
Afdeling_object VARCHAR(100),
|
||||
PO_Code VARCHAR(50),
|
||||
Omschrijving_PO TEXT,
|
||||
Vervaldatum DATE,
|
||||
Frequentie INT,
|
||||
UOM TEXT,
|
||||
PO_schema_Niet_gebruikt TEXT,
|
||||
Cluster TEXT,
|
||||
Locatie TEXT,
|
||||
Locatie_omschrijving TEXT,
|
||||
Klasse_object TEXT,
|
||||
Categorie TEXT
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
# Data in de database invoegen
|
||||
for _, row in df.iterrows():
|
||||
for column in row.index:
|
||||
if pd.isna(row[column]):
|
||||
row[column] = ""
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO onderhoud
|
||||
(Object_code, Object_omschrijving, Afdeling_object, PO_Code, Omschrijving_PO, Vervaldatum, Frequentie,
|
||||
UOM, PO_schema_Niet_gebruikt, Cluster, Locatie, Locatie_omschrijving, Klasse_object, Categorie)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
row["Object code"],
|
||||
row["Object omschrijving"],
|
||||
row["Afdeling object"],
|
||||
row["PO Code"],
|
||||
row["Omschrijving PO"],
|
||||
row["Vervaldatum"],
|
||||
row["Frequentie"],
|
||||
row["UOM"],
|
||||
row["PO-schema Niet gebruikt"],
|
||||
row["Cluster"],
|
||||
row["Locatie"],
|
||||
row["Locatie omschrijving"],
|
||||
row["Klasse object"],
|
||||
row["Categorie"],
|
||||
),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||
app.run(debug=True)
|
||||
@ -0,0 +1,6 @@
|
||||
flask
|
||||
python-dotenv
|
||||
pandas
|
||||
mariadb
|
||||
openpyxl
|
||||
werkzeug
|
||||
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start de cron-demon
|
||||
cron
|
||||
|
||||
# Start het Python-script currentprice.py
|
||||
python /app/ppo_insight.py &
|
||||
|
||||
# Wacht voor altijd zodat de container draaiende blijft
|
||||
tail -f /dev/null
|
||||
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Bestand Uploaden</title>
|
||||
<!-- Bootstrap CSS toevoegen -->
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1>Upload een bestand</h1>
|
||||
|
||||
<!-- Flash-meldingen weergeven -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="alert alert-dismissible fade show mt-3" role="alert">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }} d-flex justify-content-between align-items-center">
|
||||
<span>{{ message|safe }}</span>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Upload formulier -->
|
||||
<form action="/" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="file">Kies een bestand:</label>
|
||||
<input type="file" name="file" id="file" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Uploaden</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS toevoegen -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="nl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Resultaat</title>
|
||||
|
||||
<!-- Bootstrap CSS toevoegen -->
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
/* Verklein de fontgrootte en de padding van de tabel */
|
||||
.table-sm th, .table-sm td {
|
||||
padding: 0.3rem;
|
||||
font-size: 0.85rem; /* Verklein de tekst */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1>Resultaten van de query</h1>
|
||||
|
||||
<!-- Link terug naar uploadpagina -->
|
||||
<a href="{{ url_for('upload_file') }}" class="btn btn-secondary mt-3">Terug naar upload</a>
|
||||
|
||||
<!-- Formulier voor het instellen van x -->
|
||||
<form method="POST" class="mt-3">
|
||||
<div class="form-group">
|
||||
<label for="x">Aantal jaren vooruitkijken (x):</label>
|
||||
<input type="number" id="x" name="x" value="{{ x }}" min="1" class="form-control w-auto d-inline">
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<a href="{{ url_for('download_excel', x=x) }}" class="btn btn-success">
|
||||
Download als Excel ({{ x }} jaar)
|
||||
</a>
|
||||
|
||||
<!-- Resultaat Tabel -->
|
||||
<table class="table table-sm table-bordered table-striped mt-3">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Object_code</th>
|
||||
<th>Object_omschrijving</th>
|
||||
<th>Afdeling_object</th>
|
||||
<th>PO_Code</th>
|
||||
<th>Omschrijving_PO</th>
|
||||
<th>Vervaldatum</th>
|
||||
<th>Frequentie</th>
|
||||
<th>UOM</th>
|
||||
<th>PO_schema_Niet_gebruikt</th>
|
||||
<th>Cluster</th>
|
||||
<th>Locatie</th>
|
||||
<th>Locatie_omschrijving</th>
|
||||
<th>Klasse_object</th>
|
||||
<th>Categorie</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in result %}
|
||||
<tr>
|
||||
<td>{{ row['Object_code'] }}</td>
|
||||
<td>{{ row['Object_omschrijving'] }}</td>
|
||||
<td>{{ row['Afdeling_object'] }}</td>
|
||||
<td>{{ row['PO_Code'] }}</td>
|
||||
<td>{{ row['Omschrijving_PO'] }}</td>
|
||||
<td>{{ row['Vervaldatum'] }}</td>
|
||||
<td>{{ row['Frequentie'] }}</td>
|
||||
<td>{{ row['UOM'] }}</td>
|
||||
<td>{{ row['PO_schema_Niet_gebruikt'] }}</td>
|
||||
<td>{{ row['Cluster'] }}</td>
|
||||
<td>{{ row['Locatie'] }}</td>
|
||||
<td>{{ row['Locatie_omschrijving'] }}</td>
|
||||
<td>{{ row['Klasse_object'] }}</td>
|
||||
<td>{{ row['Categorie'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS toevoegen -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Loading…
Reference in new issue