feat: write template
This commit is contained in:
commit
a0a6d2cf84
98 files changed
+6609
No files matched your search
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import datetime
|
||||
|
||||
# Load the file
|
||||
file_path = 'Zeitplan.xlsx'
|
||||
df = pd.read_excel(file_path, header=[1, 2, 3], index_col=0)
|
||||
|
||||
# 1. Tag Rows as 'should' (Plan) or 'is' (Actual)
|
||||
# Assumption: Rows with a Task Name are 'should'. Empty rows below them are 'is'.
|
||||
df['Type'] = np.where(df.index.notna(), 'should', 'is')
|
||||
|
||||
# 2. Propagate Task Names to the 'is' rows
|
||||
# We forward-fill the index so the empty 'IST' row gets the name of the task above it
|
||||
df.index = df.index.to_series().ffill()
|
||||
|
||||
# 3. Cleanup Garbage
|
||||
# Remove the header row itself ('Tätigkeit') and any top-level empty rows
|
||||
df = df.drop('Tätigkeit', errors='ignore')
|
||||
df = df[df.index.notna()] # Drop rows that were empty at the very top before any task
|
||||
|
||||
# 4. Save Original Order
|
||||
original_task_order = df.index.unique().tolist()
|
||||
|
||||
# 5. Separate Time Grid
|
||||
# We take columns from index 4 onwards. We must exclude our new 'Type' column from the data grid.
|
||||
# (The 'Type' column is at the very end now)
|
||||
time_grid = df.iloc[:, 4:-1]
|
||||
row_types = df['Type'] # Save the types to re-attach
|
||||
|
||||
# 6. Unpivot (Melt)
|
||||
# We add 'Type' to the index temporarily so it sticks with the data during stacking
|
||||
time_grid = time_grid.set_index(row_types, append=True)
|
||||
df_long = time_grid.stack(level=[0, 1, 2], future_stack=True).reset_index()
|
||||
|
||||
# Rename columns (Index 0=Task, Index 1=Type, followed by the stacked levels)
|
||||
df_long.columns = ['Task', 'Type', 'KW', 'Date', 'Halbtag', 'Hours']
|
||||
|
||||
# 7. Clean Data
|
||||
df_long['Hours'] = pd.to_numeric(df_long['Hours'], errors='coerce').fillna(0)
|
||||
|
||||
# 8. Pivot to get 'should' and 'is' side-by-side
|
||||
# We group by Date/Task and sum up the 'should' hours and 'is' hours separately
|
||||
df_pivot = df_long.pivot_table(
|
||||
index=['Date', 'KW', 'Task'],
|
||||
columns='Type',
|
||||
values='Hours',
|
||||
aggfunc='sum'
|
||||
).reset_index()
|
||||
|
||||
# Ensure we have both columns (fill with 0 if missing)
|
||||
for col in ['should', 'is']:
|
||||
if col not in df_pivot.columns:
|
||||
df_pivot[col] = 0.0
|
||||
df_pivot = df_pivot.fillna(0)
|
||||
|
||||
# 9. Apply Order & Sort
|
||||
df_pivot['Task'] = pd.Categorical(df_pivot['Task'], categories=original_task_order, ordered=True)
|
||||
df_pivot['Date'] = pd.to_datetime(df_pivot['Date'], errors='coerce')
|
||||
|
||||
# Remove the 'Total' row and any rows where Task might be 'Total' or NaN
|
||||
df_final = df_pivot[
|
||||
(df_pivot['Task'].notna()) &
|
||||
(~df_pivot['Task'].astype(str).str.contains('Total|Summe|Tätigkeit', case=False))
|
||||
].copy()
|
||||
|
||||
# Filter out rows where NOTHING happened (neither plan nor actual)
|
||||
df_final = df_pivot[(df_pivot['should'] > 0) | (df_pivot['is'] > 0)].copy()
|
||||
df_final = df_final.sort_values(by=['Date', 'Task'])
|
||||
|
||||
# --- YAML Output ---
|
||||
print("### last updated at: " + datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S") + " ###\n")
|
||||
|
||||
current_date = None
|
||||
|
||||
for _, row in df_final.iterrows():
|
||||
# Format date
|
||||
date_str = f"{row['Date'].day}-{row['Date'].month}-{row['Date'].year}"
|
||||
|
||||
if date_str != current_date:
|
||||
current_date = date_str
|
||||
print(f"{date_str}:")
|
||||
|
||||
# Convert Hours to Minutes
|
||||
should_min = int(row['should'] * 60)
|
||||
is_min = int(row['is'] * 60)
|
||||
|
||||
print(f" - name: {row['Task']}")
|
||||
print(f" should: {should_min}")
|
||||
print(f" is: {is_min}")
|
||||
@@ -0,0 +1,3 @@
|
||||
pandas>=2.1.0
|
||||
openpyxl
|
||||
numpy
|
||||
Reference in new issue
Block a user