{ "cells": [ { "cell_type": "code", "execution_count": 11, "id": "efde98a5", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import datetime as dt\n", "import openpyxl.styles" ] }, { "cell_type": "code", "execution_count": 12, "id": "8e84cb92", "metadata": {}, "outputs": [], "source": [ "def get_week_boundary(target_date, boundary='first'):\n", " \"\"\"\n", " Returns the Monday (first) or Sunday (last) of the week for a given date.\n", " \n", " :param target_date: datetime object\n", " :param boundary: 'first' or 'last'\n", " :return: datetime object\n", " \"\"\"\n", " # .weekday() returns 0 for Monday, 6 for Sunday\n", " days_since_monday = target_date.weekday()\n", " \n", " if boundary == 'first':\n", " return target_date - dt.timedelta(days=days_since_monday)\n", " elif boundary == 'last':\n", " days_until_sunday = 6 - days_since_monday\n", " return target_date + dt.timedelta(days=days_until_sunday)\n", " else:\n", " raise ValueError(\"Boundary must be 'first' or 'last'\")" ] }, { "cell_type": "code", "execution_count": 13, "id": "7a9ce99f", "metadata": {}, "outputs": [], "source": [ "year = 2026\n", "\n", "date_index = pd.date_range(get_week_boundary(dt.datetime(year,1,1),'first'),get_week_boundary(dt.datetime(year,12,31),'last'),freq='1D')\n", "\n", "columns = ['KW','date','weekday','location','trainer','meetup_time','start_time','warmup','main','cooldown','post_workout','approx_end']\n", "columns = columns + [f'padding_{i}' for i in range(5)]\n", "header_names = ['KW','Datum','Tag','Ort','Trainer:in','Treffpunkt','Trainingsbeginn','Warm-Up','Programm','Cool-Down','Post-Workout','Trainingsende']\n", "header_names = header_names + ['' for i in range(5)]\n", "\n", "\n", "df = pd.DataFrame(index=date_index,columns=columns)" ] }, { "cell_type": "code", "execution_count": 14, "id": "7be00de3", "metadata": {}, "outputs": [], "source": [ "weekday_dict_short = {0: 'Mo',\n", " 1: 'Di',\n", " 2: 'Mi',\n", " 3: 'Do',\n", " 4: 'Fr',\n", " 5: 'Sa',\n", " 6: 'So'}\n", "\n", "weekday_dict_long = {0: 'Montag',\n", " 1: 'Dienstag',\n", " 2: 'Mittwoch',\n", " 3: 'Donnerstag',\n", " 4: 'Freitag',\n", " 5: 'Samstag',\n", " 6: 'Sonntag'}" ] }, { "cell_type": "code", "execution_count": 15, "id": "3f25dd5f", "metadata": {}, "outputs": [], "source": [ "df['KW'] = df.index.isocalendar().week\n", "df['date'] = df.index.strftime('%d.%m.%Y')\n", "df['weekday'] = df.index.weekday\n", "df['weekday'] = df['weekday'].replace(weekday_dict_short)\n", "df['location'] = 'RVV'" ] }, { "cell_type": "code", "execution_count": 16, "id": "2b98908c", "metadata": {}, "outputs": [], "source": [ "def get_month_slice(df,month_number):\n", "\n", " week_numbers_of_month = df.loc[df.index.month==month_number,'KW'].unique()\n", "\n", " if month_number == 1:\n", " week_numbers_of_month = [week_number if (week_number < 6) else None for week_number in week_numbers_of_month]\n", " elif month_number == 12:\n", " week_numbers_of_month = [week_number if (week_number > 40) else None for week_number in week_numbers_of_month]\n", "\n", " df_slice = df.loc[df['KW'].isin(week_numbers_of_month),:]\n", " return df_slice" ] }, { "cell_type": "code", "execution_count": 17, "id": "69339931", "metadata": {}, "outputs": [], "source": [ "def highlight_weekends(row):\n", " # .weekday() > 4 means Saturday (5) or Sunday (6)\n", " if row.name.weekday() > 4:\n", " return ['background-color: #ffcccc'] * len(row) # Light red/pink\n", " return [''] * len(row)" ] }, { "cell_type": "code", "execution_count": 18, "id": "379682df", "metadata": {}, "outputs": [], "source": [ "def format_rows(row):\n", " # .weekday() > 4 means Saturday (5) or Sunday (6)\n", " if row.name.weekday() == 0:\n", " bg_style = 'background-color: #D1D1D1'\n", " border_style = 'border-bottom: 1pt solid black; border-left: 1pt solid black'\n", " elif row.name.weekday() == 1:\n", " bg_style = ''\n", " border_style = 'border-bottom: 1pt solid black; border-left: 1pt solid black'\n", " elif row.name.weekday() == 2:\n", " bg_style = 'background-color: #D1D1D1'\n", " border_style = 'border-bottom: 1pt solid black; border-left: 1pt solid black'\n", " elif row.name.weekday() == 3:\n", " bg_style = ''\n", " border_style = 'border-bottom: 1pt solid black; border-left: 1pt solid black'\n", " elif row.name.weekday() == 4:\n", " bg_style = 'background-color: #D1D1D1'\n", " border_style = 'border-bottom: 2pt solid black; border-left: 1pt solid black'\n", " elif row.name.weekday() == 5:\n", " bg_style = 'background-color: ##C5D9F1'\n", " border_style = 'border-bottom: 1pt solid black; border-left: 1pt solid black'\n", " elif row.name.weekday() == 6:\n", " bg_style = 'background-color: ##C5D9F1'\n", " border_style = 'border-bottom: 3pt solid black; border-left: 1pt solid black'\n", "\n", " # Combine them (separated by semicolon)\n", " combined = f\"{bg_style}; {border_style}\"\n", " return [combined] * len(row)" ] }, { "cell_type": "code", "execution_count": 21, "id": "ae3bf250", "metadata": {}, "outputs": [], "source": [ "months = range(1,12+1,1)\n", "file_name = f\"Trainingsplan_{year}.xlsx\"\n", "\n", "with pd.ExcelWriter(file_name, engine='openpyxl') as writer:\n", " for month in months:\n", " df_month = get_month_slice(df, month)\n", " \n", " # 1 format the cells\n", " \n", " # 1.1 format the rows (background color and borders)\n", " styled_df = df_month.style.apply(format_rows, axis=1)\n", " \n", " # 1.2 center all cells\n", " styled_df = styled_df.set_properties(**{'text-align': 'center'})\n", " \n", " # 2 Write to xlsx\n", " styled_df.to_excel(writer, sheet_name=f\"{month}_{year}\",index=False,header=header_names)\n", "\n", " # 3 after writing, change other formats\n", " # --- Access the openpyxl worksheet object ---\n", " worksheet = writer.sheets[f\"{month}_{year}\"]\n", "\n", " # 3.1 set the widths\n", " worksheet.column_dimensions['A'].width = 18*0.35\n", " worksheet.column_dimensions['B'].width = 18*0.89\n", " worksheet.column_dimensions['C'].width = 18*0.35\n", " worksheet.column_dimensions['D'].width = 18*0.40\n", " worksheet.column_dimensions['E'].width = 18*0.73\n", " worksheet.column_dimensions['F'].width = 18*0.81\n", " worksheet.column_dimensions['G'].width = 18*1.05\n", " worksheet.column_dimensions['H'].width = 18*0.76\n", " worksheet.column_dimensions['I'].width = 18*2.50\n", " worksheet.column_dimensions['J'].width = 18*0.76\n", " worksheet.column_dimensions['K'].width = 18*1.05\n", " worksheet.column_dimensions['L'].width = 18*1.05\n", "\n", " # 3.2 format the header\n", " # Define a bold, larger font\n", " header_font = openpyxl.styles.Font(name='Arial', size=12, bold=True, color=\"000000\")\n", " \n", " # Iterate through the first row (Header)\n", " # worksheet[1] returns all cells in the first row\n", " for cell in worksheet[1]:\n", " cell.font = header_font\n", " cell.alignment = openpyxl.styles.Alignment(horizontal='center', vertical='center')\n", " cell.border = openpyxl.styles.Border(bottom=openpyxl.styles.Side(border_style='medium', color='000000'))\n", "\n", " # 3.3 freeze the first row\n", " worksheet.freeze_panes = 'A2' " ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }