move files
move scripts
This commit is contained in:
1 parent
5cdbf51c1e
commit
408566b6ea
21 files changed
+53
No files matched your search
Executable
+172
@@ -0,0 +1,172 @@
|
||||
import pandas as pd
|
||||
import pprint
|
||||
from datetime import datetime
|
||||
import json
|
||||
# Create a PrettyPrinter with custom settings
|
||||
|
||||
pp = pprint.PrettyPrinter(
|
||||
indent=2,
|
||||
width=100000, # large line width to avoid wrapping
|
||||
compact=False, # don't compress structures
|
||||
depth=None, # don't limit nesting depth
|
||||
sort_dicts=False
|
||||
)
|
||||
# load data
|
||||
absences = pd.read_excel("Activity Timeoff 2025_01_01 - 2026_02_28.xlsx")
|
||||
# Load Excel (or CSV) file
|
||||
# For Excel: timesheet = pd.read_excel("timesheet.xlsx", engine="openpyxl")
|
||||
timesheets = pd.read_excel("[ALL] Template_Time_Sheets_FFG - Unified.xlsx", sheet_name=None)
|
||||
df_timesheet = timesheets["2025"]
|
||||
|
||||
## Sort RATES
|
||||
df_rates = timesheets["Stundensätze"]
|
||||
# Remove all non-numeric characters (keep digits and dot)
|
||||
hourlyRateKey = "Hourly Rate for Application €/h"
|
||||
df_rates = df_rates.rename(columns={hourlyRateKey: 'rate'})
|
||||
hourlyRateKey = 'rate'
|
||||
df_rates[hourlyRateKey] = df_rates[hourlyRateKey].astype(str).str.replace(r'[^0-9.]', '', regex=True)
|
||||
# Ensure the Hourly Rate column is numeric
|
||||
df_rates[hourlyRateKey] = pd.to_numeric(df_rates[hourlyRateKey], errors='coerce')
|
||||
|
||||
|
||||
## FORMAT timesheet data frame
|
||||
# Fill missing Activity for earlier rows if necessary
|
||||
df_timesheet['Activity'] = df_timesheet['Activity'].ffill() # forward-fill the activity for blank cells
|
||||
# Convert 'Date' to datetime
|
||||
df_timesheet['Date'] = pd.to_datetime(df_timesheet['Date'], errors='coerce')
|
||||
# Forward-fill missing dates and activities
|
||||
df_timesheet['Date'] = df_timesheet['Date'].ffill()
|
||||
df_timesheet['Activity'] = df_timesheet['Activity'].ffill()
|
||||
# Filter out rows where 'Person' is missing
|
||||
df_timesheet_filtered = df_timesheet.dropna(subset=['Person']).copy()
|
||||
|
||||
|
||||
# #FILETER ABSECES
|
||||
absences['Start date'] = pd.to_datetime(absences['Start date'], errors='coerce')
|
||||
absences['End date'] = pd.to_datetime(absences['End date'], errors='coerce')
|
||||
df_absences_filtered = absences
|
||||
df_absences_by_year = {}
|
||||
for year in df_absences_filtered['Start date'].dt.year.unique():
|
||||
# Filter for the year
|
||||
df_year = df_absences_filtered[df_absences_filtered['Start date'].dt.year == year].copy()
|
||||
# Combine first and last name into a new column 'Person'
|
||||
df_year['Person'] = df_year['Last name'].astype(str) + ", " + df_year['First name'].astype(str)
|
||||
# Select and reorder columns
|
||||
df_year = df_year[['Person', 'Start date', 'End date']]
|
||||
# Reset index
|
||||
df_year = df_year.reset_index(drop=True)
|
||||
# Store in dictionary
|
||||
df_absences_by_year[year] = df_year
|
||||
|
||||
for year, df in df_absences_by_year.items():
|
||||
# Function to find matching full name(s) for a person
|
||||
def get_short_name(person):
|
||||
# Remove spaces from person
|
||||
person_clean = str(person).replace(" ", "")
|
||||
# Remove spaces from df_rates['Name'] and check for match
|
||||
matches = df_rates[df_rates['Name'].str.replace(" ", "", regex=False)
|
||||
.str.contains(person_clean, case=False, na=False)]
|
||||
if not matches.empty:
|
||||
# Return only the first match
|
||||
return str(matches['Short'].iloc[0])
|
||||
|
||||
return "" # no match found
|
||||
# Apply the function row-wise to create a new column
|
||||
df['Short'] = df['Person'].apply(get_short_name)
|
||||
|
||||
# Keep only the row with the highest Hourly Rate per Name
|
||||
df_rates = df_rates.loc[df_rates.groupby('Short')[hourlyRateKey].idxmax()].reset_index(drop=True)
|
||||
|
||||
# Sort By Year
|
||||
# Create dictionary of DataFrames for each year
|
||||
df_timesheet_by_year = {}
|
||||
for year in df_timesheet_filtered['Date'].dt.year.unique():
|
||||
# Filter for the year
|
||||
df_year = df_timesheet_filtered[df_timesheet_filtered['Date'].dt.year == year].copy()
|
||||
# Convert 'Workpackage' to integer (nullable Int64 to handle NaN safely)
|
||||
df_year['Workpackage'] = pd.to_numeric(df_year['Workpackage'], errors='coerce').astype('Int64')
|
||||
# Select and reorder columns
|
||||
df_year = df_year[['Person', 'Date', 'Workpackage', 'Time (in hours)', 'Activity']]
|
||||
# Reset index
|
||||
df_year = df_year.reset_index(drop=True)
|
||||
# Store in dictionary
|
||||
df_timesheet_by_year[year] = df_year
|
||||
|
||||
for year, df in df_timesheet_by_year.items():
|
||||
# Function to find matching full name(s) for a person
|
||||
def get_full_name(person):
|
||||
matches = df_rates[df_rates['Short'].str.contains(str(person), case=False, na=False)]
|
||||
if not matches.empty:
|
||||
return str(matches['Name'].iloc[0])
|
||||
return "" # no match found
|
||||
# Apply the function row-wise to create a new column
|
||||
df['FullName'] = df['Person'].apply(get_full_name)
|
||||
|
||||
|
||||
|
||||
# Build nested dictionary
|
||||
data = {}
|
||||
for year in df_timesheet_by_year.keys():
|
||||
for _, row in df_timesheet_by_year[year].iterrows():
|
||||
person = row['Person']
|
||||
date = row['Date']
|
||||
date_str = date.strftime("%Y-%m-%d")
|
||||
# Skip rows without a person
|
||||
if pd.isna(person):
|
||||
continue
|
||||
|
||||
data.setdefault(year, {}).setdefault(person, {}).setdefault("absences", [])
|
||||
data.setdefault(year, {}).setdefault(person, {}).setdefault("workingdays", {})
|
||||
data[year][person]["workingdays"][date_str] = {
|
||||
"workpackage": row['Workpackage'],
|
||||
"time": row['Time (in hours)'],
|
||||
"activity": row['Activity']
|
||||
}
|
||||
|
||||
# Group by Person and Workpackage and sum hours
|
||||
summary = df_timesheet_by_year[year].groupby(['FullName', 'Workpackage'])['Time (in hours)'].sum().reset_index()
|
||||
# Merge hourly rate into summary using the full name
|
||||
summary = summary.merge(
|
||||
df_rates[['Name', hourlyRateKey]],
|
||||
left_on='FullName',
|
||||
right_on='Name',
|
||||
how='left'
|
||||
)
|
||||
# Compute costs
|
||||
summary['costs'] = summary['Time (in hours)'] * summary[hourlyRateKey]
|
||||
# Show all columns and rows
|
||||
print(summary.to_string(index=False)) # index=False hides row numbers
|
||||
|
||||
for year in df_absences_by_year.keys():
|
||||
for _, row in df_absences_by_year[year].iterrows():
|
||||
person = row['Short']
|
||||
start_date = row['Start date']
|
||||
end_date = row['End date']
|
||||
|
||||
# Ensure nested structure exists
|
||||
data.setdefault(year, {}).setdefault(person, {}).setdefault("absences", [])
|
||||
data.setdefault(year, {}).setdefault(person, {}).setdefault("workingdays", {})
|
||||
data[year][person]["absences"].append({"start": start_date, "end": end_date})
|
||||
|
||||
for year in data.keys():
|
||||
for person in data[year].keys():
|
||||
for absence in data[year][person]["absences"]:
|
||||
# Generate all dates between start and end
|
||||
all_dates = pd.date_range(start=absence["start"], end=absence["end"], freq='B') # 'B' = business days (Mon-Fri)
|
||||
# Create a dict with dates as keys and empty dict as value
|
||||
workdays_dict = set(data[year][person]['workingdays'].keys())
|
||||
absence_dict = {date.strftime("%Y-%m-%d"): {} for date in all_dates}
|
||||
|
||||
# Get sets of dates
|
||||
absence_dates = set(absence_dict)
|
||||
working_dates = set(data[year][person]['workingdays'].keys())
|
||||
|
||||
|
||||
# Find intersection
|
||||
overlap = absence_dates & working_dates
|
||||
sorted_overlap = sorted(overlap, key=lambda x: datetime.strptime(x, "%Y-%m-%d"))
|
||||
if overlap and absence["start"] != absence["end"]:
|
||||
print(f"Dates overlapping for {person}: {overlap} - Vac: {absence["start"].strftime("%Y-%m-%d")} - {absence["end"].strftime("%Y-%m-%d")}")
|
||||
elif overlap:
|
||||
print(f"Dates overlapping for {person}: {overlap} - Vac: {absence["start"].strftime("%Y-%m-%d")}")
|
||||
print("fin")
|
||||
Reference in new issue
Block a user