initial commit
This commit is contained in:
+41
@@ -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.
@@ -0,0 +1,3 @@
|
||||
@echo on
|
||||
python src\main.py %*
|
||||
pause
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"merge_groups": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
# Python-Abhaengigkeiten
|
||||
# Installation: pip install -r requirements.txt
|
||||
|
||||
pandas>=2.0.0
|
||||
openpyxl>=3.1.0
|
||||
+175
@@ -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()
|
||||
Reference in New Issue
Block a user