initial commit

This commit is contained in:
2026-05-06 14:35:12 +02:00
commit 573ccd7c61
8 changed files with 375 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
*.pyc.*
# Virtual Environments
.venv/
venv/
env/
ENV/
# Distribution / Packaging
build/
dist/
*.egg-info/
# Logs
*.log
# OS / Editor
.DS_Store
Thumbs.db
*.swp
*.swo
# IDEs
.vscode/
.idea/
# Projekt-spezifisch
# Input-Dateien (Rohdaten, nicht versionieren)
input/*
# Output-Dateien (generierte Ergebnisse)
output/*
# Optional: config ignorieren, falls sensitiv
# config/config.json
Binary file not shown.
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
@echo on
python src\main.py %*
pause
+5
View File
@@ -0,0 +1,5 @@
{
"merge_groups": [
]
}
+146
View File
@@ -0,0 +1,146 @@
# Auswertung Automationskalkulation
Dieses Repository enthält ein Python-Skript zur automatisierten Auswertung von Excel-Dateien. Die Verarbeitung erfolgt entweder über einen Input-Ordner oder durch Übergabe einzelner Dateien per Kommandozeile.
---
## Projektstruktur
```text
.
├── src/
│ └── main.py
├── input/
│ └── Excel-Dateien zur Verarbeitung
├── output/
│ └── erzeugte Ergebnisdateien
├── config/
│ └── config.json
├── run.bat
└── README.md
```
---
## Voraussetzungen
- Python 3.9 oder höher
- Abhängigkeiten:
- pandas
- openpyxl
Installation der Abhängigkeiten:
```text
pip install pandas openpyxl
```
---
## Nutzung
### Variante 1: Verarbeitung über Input-Ordner
1. Excel-Dateien in das Verzeichnis `input/` legen
2. `run.bat` ausführen
Alle Dateien im Input-Ordner werden verarbeitet.
---
### Variante 2: Verarbeitung einzelner Dateien
Excel-Dateien auf `run.bat` ziehen.
Nur die übergebenen Dateien werden verarbeitet.
---
## Ausgabe
Die Ergebnisdateien werden im Verzeichnis `output/` gespeichert.
Der Dateiname hat folgendes Format:
```text
{inputname}*auswertung*{timestamp}.xlsx
```
Inhalt der Ausgabe:
- aggregierte Stunden pro Sivas:AG
- Detailaufstellungen je Eintrag
- zusätzliche Werte aus der ersten Datei
---
## Konfiguration
Die Datei `config/config.json` enthält Konfigurationsparameter.
Beispiel:
```json
{
"merge_groups": [
["25", "26"],
["47/101", "50/111"]
]
}
```
Die definierten Sivas-Arbeitsgänge werden bei der Zusammenführung von Schlüsseln berücksichtigt. Dabei werden alle Zeilen mit gleichen Werten der Spalte D ("Sivas:AG") zusammengeführt.
Beispiel:
- ["25", "26"]: "Montage Elektrisch/Inbetriebnahmetest Hardware durch Montage" (Sivas AG 25) und "Maschinentest/Inbetriebnahmeunterstützung durch Montage" (Sivas AG 26)
- ["47/101", "50/111"]: "SPS-Inbetriebsetzung" (Sivas AG 47/101) und "PC-Inbetriebsetzung" (Sivas AG 50/111)
---
## Verarbeitungslogik
Das Skript verarbeitet definierte Zellbereiche aus Excel-Dateien:
- Zeilen 28 bis 56
- Spalte D: Schlüssel (Sivas:AG)
- Spalte F: Stunden
- Spalte A: Beschreibung
Die Stundenwerte werden gerundet und pro Schlüssel aggregiert.
---
## Fehlerquellen
Keine Ausgabe oder leere Verarbeitung kann folgende Ursachen haben:
- Input-Verzeichnis ist leer
- keine gültigen Excel-Dateien vorhanden
- temporäre Excel-Dateien (`~$...`) werden ignoriert
- falsches Arbeitsverzeichnis beim Start
---
## Ausführungsvoraussetzung im Skript
Die Datei `main.py` muss folgenden Einstiegspunkt enthalten:
```python
if __name__ == "__main__":
main()
```
---
## Hinweis
Das Skript ist für die Batch-Verarbeitung von Dateien vorgesehen und verfügt über keine grafische Oberfläche.
---
+5
View File
@@ -0,0 +1,5 @@
# Python-Abhaengigkeiten
# Installation: pip install -r requirements.txt
pandas>=2.0.0
openpyxl>=3.1.0
+175
View File
@@ -0,0 +1,175 @@
import pandas as pd
import os
import sys
import json
import math
from datetime import datetime
from openpyxl.styles import Alignment
from openpyxl.utils import get_column_letter
def load_config():
with open(os.path.join("config", "config.json"), "r", encoding="utf-8") as f:
return json.load(f)
def normalize_key(value, merge_groups):
value = str(value)
for group in merge_groups:
if value in group:
return ", ".join(merge_groups)
return value
def safe_hours(value, key_raw):
try:
val = float(value)
return math.ceil(val), None
except:
return 0, f"Sivas:AG_{key_raw}"
def process_files(input_paths):
config = load_config()
merge_groups = config.get("merge_groups", [])
result = {}
for input_path in input_paths:
df = pd.read_excel(input_path, header=None)
df_slice = df.iloc[28:56]
for _, row in df_slice.iterrows():
key_raw = row[3] # Spalte D
hours_raw = row[5] # Spalte F
desc = row[0] # Spalte A
# 🔹 NEU: komplett leere Zeilen ignorieren
if pd.isna(key_raw) and pd.isna(hours_raw):
continue
key_raw = str(key_raw)
hours, error_key = safe_hours(hours_raw, key_raw)
if error_key:
key = error_key
else:
key = normalize_key(key_raw, merge_groups)
if key not in result:
result[key] = {
"hours": 0,
"details": [],
"descriptions": []
}
result[key]["hours"] += hours
result[key]["descriptions"].append(str(desc))
if error_key:
result[key]["details"].append(f"FEHLER: {hours_raw} ({desc})")
else:
result[key]["details"].append(f"{hours}h {desc}")
# Ergebnis bauen
output_rows = []
total_sum = 0
for key, data in result.items():
total_sum += data["hours"]
# 🔹 NEU: SUMME in Details
details_text = "\n".join(data["details"])
details_text += f"\n{data['hours']}h SUMME"
descriptions_text = "\n".join(data["descriptions"])
output_rows.append({
"Sivas:AG": key,
"Summe Stunden": data["hours"],
"Details": details_text,
"Beschreibungen": descriptions_text
})
output_df = pd.DataFrame(output_rows)
# Zusatzwerte nur aus erster Datei (sonst Chaos)
df_first = pd.read_excel(input_paths[0], header=None)
extra_values = df_first.iloc[23:27, 4]
extra_df = pd.DataFrame({
"Wert": extra_values.values
})
now = datetime.now().strftime("%Y%m%d_%H%M%S")
if len(input_paths) == 1:
base_name = os.path.splitext(os.path.basename(input_paths[0]))[0]
else:
base_name = "multi_file"
output_path = os.path.join("output", f"{base_name}_auswertung_{now}.xlsx")
with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
output_df.to_excel(writer, sheet_name="Arbeitsgänge", index=False)
extra_df.to_excel(writer, sheet_name="Material", index=False)
#workbook = writer.book
worksheet = writer.sheets["Arbeitsgänge"]
wrap_alignment = Alignment(wrap_text=True, vertical="top")
# Spalten C und D (0-basiert: C=2, D=3)
target_cols = [2, 3]
for row in worksheet.iter_rows(min_row=2): # Header überspringen
for col_idx in target_cols:
cell = row[col_idx]
cell.alignment = wrap_alignment
worksheet.column_dimensions["A"].width = 25
worksheet.column_dimensions["B"].width = 15
worksheet.column_dimensions["C"].width = 50
worksheet.column_dimensions["D"].width = 50
worksheet.freeze_panes = "A2"
worksheet.auto_filter.ref = worksheet.dimensions
print("\n=== Kontrollübersicht ===")
print(f"Gesamtsumme Stunden: {total_sum}")
print("\nDetails pro Sivas-AG:")
for key, data in result.items():
print(f"{key}: {data['hours']}h")
print(f"\nDatei gespeichert unter: {output_path}")
def main():
print("CWD:", os.getcwd())
input_paths = sys.argv[1:]
# 🔹 Wenn keine Dateien per Drag & Drop kommen → input Ordner nutzen
if not input_paths:
input_dir = os.path.join(os.getcwd(), "input")
print("Working dir:", os.getcwd())
print("Input dir:", input_dir)
if not os.path.exists(input_dir):
print("Input-Ordner existiert nicht.")
return
input_paths = [
os.path.join(input_dir, f)
for f in os.listdir(input_dir)
if f.lower().endswith(".xlsx") or f.lower().endswith(".xls")
]
valid_paths = [p for p in input_paths if os.path.exists(p)]
if not valid_paths:
print("Keine gültigen Dateien gefunden.")
return
process_files(valid_paths)
if __name__ == "__main__":
main()