{
"cells": [
{
"cell_type": "markdown",
"id": "c2aeb8da-b2c6-4399-a832-c843c484e75f",
"metadata": {},
"source": [
"<h2 style=\"text-align:left;\">Filtering & Formatting - WIP - Priority database, Summary, Snapshot, Backlog, TurnoverReport, Dashboard included </h2> "
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1e2da197-de01-467d-a24e-cc551a1c5155",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Inventory file Date: 10-30-2024\n",
"Workbook state after save and close: ['Gantt']\n",
"Initialisation of the Transfer Project Overview spreadsheet ... Processing ...\n",
" |CM-Inventory| & |CM-BOM| saved to 'CM-Transfer_Project-Overview_10-31-2024.xlsx'.\n",
"Processing |Clear-to-Build| ...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\Tanguy.Boete\\AppData\\Local\\Temp\\ipykernel_27888\\3416105912.py:300: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n",
" df_CTB_updated = df_CTB_updated.groupby(['IDD Top Level', 'IDD Component'], as_index=False).apply(select_row_with_priority)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of rows in df_CTB_updated after keeping only unique 'IDD Component' per 'IDD Top Level': 12663\n",
"Max Qty (GS) filled.\n",
"Top Level sharing Components filled and saved to CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Cost cancelation impact filled.\n",
"Number of missing components: 2676\n",
"Number of rows in |Clear-to-Build| before including missing component: 12663\n",
"Number of rows in |Clear-to-Build| after appending missing components - Including Make Part: 15339\n",
"Missing components from |CM-BOM| have been included in |Clear-to-Build|.\n",
"Max Qty (GS) updated.\n",
"Max Qty (GS) updated with Floor stock Item and Make Part from CUU\n",
"|Clear-to-Build| after updating Max Qty (GS) and handling Pur/Mfg == 'M' condition.\n",
"Remaining critical quantity filled for inventory status 'Component not in Inventory'.\n",
"Floor-stock CUU identified for relevant rows.\n",
"Priority database added successfully as |CM-Priority| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority']\n",
"Processing |CM-Backlog| ...\n",
"Backlog files loaded successfully.\n",
"Backlog added successfully as |CM-Backlog| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog']\n",
"Processing |CM-TurnoverReport| ...\n",
"TurnoverReport added successfully as |CM-TurnoverReport| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport']\n",
"Processing |CM-WIP| ...\n",
"WIP files loaded successfully.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\Tanguy.Boete\\AppData\\Local\\Temp\\ipykernel_27888\\3416105912.py:2274: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n",
" df_WIP_Temp = df_WIP_Temp.groupby(['WO', 'Pty Indice'], group_keys=False).apply(select_closest_to_today).reset_index(drop=True)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"WIP added successfully as |CM-WIP| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP']\n",
"Processing |PendingReport| ...\n",
"Pending Report added successfully as |PendingReport| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport']\n",
"Processing |Historic| ...\n",
"Historic added successfully as |CM-Historic| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport', 'CM-Historic']\n",
"Processing |CM-LaborReport| ...\n",
"LaborReport added successfully as |CM-LaborReport| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport', 'CM-Historic', 'CM-LaborReport']\n",
"Processing |CM-MakeArchitecutre| ...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\Tanguy.Boete\\AppData\\Local\\Temp\\ipykernel_27888\\3416105912.py:4004: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n",
" df_filtered = grouped.apply(process_group).reset_index(drop=True).copy()\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Make Architecture added successfully as |CM-MakeArchi| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport', 'CM-Historic', 'CM-LaborReport', 'CM-MakeArchitecture']\n",
"Processing |Summary| ...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\Tanguy.Boete\\AppData\\Local\\Temp\\ipykernel_27888\\3416105912.py:4591: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n",
" .apply(lambda x: x.sort_values(by='Level').sort_values(by='Priority'))\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Summary added successfully as |Summary| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Summary', 'Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport', 'CM-Historic', 'CM-LaborReport', 'CM-MakeArchitecture']\n",
"Processing |Snapshot| ...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"C:\\Users\\Tanguy.Boete\\AppData\\Local\\Temp\\ipykernel_27888\\3416105912.py:4684: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n",
" min_qty_rows = grouped.apply(lambda df: df.loc[df['Max Qty (GS)'].idxmin()]).reset_index(drop=True)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pty Indice to update: ['COMAC-New1' 'EMBRAER-15' 'EMBRAER-17' 'EMBRAER-22' 'EMBRAER-29'\n",
" 'EMBRAER-30' 'EMBRAER-New1' 'P10D' 'P14A' 'P14B' 'P19B' 'P2A' 'P2AA'\n",
" 'P2AE' 'P2AF' 'P2B' 'P2C' 'P2D' 'P2E' 'P2F' 'P2G' 'P2H' 'P2I' 'P2J' 'P2K'\n",
" 'P2Q' 'P2R' 'P2S' 'P2U' 'P2X' 'P2Y' 'P2Z' 'SIK-1' 'SIK-14' 'SIK-20']\n",
"35 rows from df_snapshot updated with df_Historic\n",
"34 rows updated with 'Completed - No Backlog'\n",
"0 rows updated with 'Not completed - No Backlog'\n",
"34 rows updated with production costs.\n",
"Snapshot added successfully as |Snapshot| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Tabs in the workbook:\n",
"['Snapshot', 'Summary', 'Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport', 'CM-Historic', 'CM-LaborReport', 'CM-MakeArchitecture']\n",
"Processing |CM-ADCNReport| ...\n",
"SEDA Shortages added successfully as|SEDA-Shortages| in CM-Transfer_Project-Overview_10-31-2024.xlsx\n",
"Workbook saved successfully.\n",
"Sheet names in CM-Transfer_Project-Overview_10-31-2024.xlsx: ['Snapshot', 'Summary', 'SEDA-Shortages', 'Gantt', 'Clear-to-Build', 'CM-Inventory', 'CM-BOM', 'CM-Priority', 'CM-Backlog', 'CM-TurnoverReport', 'CM-WIP', 'PendingReport', 'CM-Historic', 'CM-LaborReport', 'CM-MakeArchitecture', 'CM-ADCNReport']\n",
"Transfer Project Overview spreadsheet generated sucessfully!\n",
"File Inputs\\CM_Priority_Database_updated.xlsx created successfully.\n",
"File Inputs\\CM_Priority_Database.xlsx updated successfully.\n"
]
}
],
"source": [
"#Tagged Formatting\n",
"import pandas as pd\n",
"from IPython.display import display\n",
"import tkinter as tk\n",
"from tkinter import messagebox, simpledialog\n",
"import openpyxl\n",
"from openpyxl import Workbook \n",
"from openpyxl import load_workbook\n",
"from openpyxl.utils.dataframe import dataframe_to_rows\n",
"from openpyxl.styles import PatternFill, Font, Alignment, Border, Side\n",
"from datetime import datetime, date\n",
"from openpyxl.utils.cell import get_column_letter\n",
"import numpy as np\n",
"from shutil import copyfile\n",
"import os\n",
"import re\n",
"import shutil\n",
"import warnings\n",
"import xlsxwriter\n",
"from openpyxl.chart import BarChart, PieChart, Reference\n",
"from openpyxl.styles import NamedStyle\n",
"from openpyxl.formatting.rule import CellIsRule\n",
"\n",
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ######## ######### #### ## ########### ##########\n",
"## ## ### ## ## ## ## ## ##\n",
"## ## #### ######### ## ## ## ## ##\n",
"## ## ## ## ## ## #### ## ##\n",
"## ######## ## ## ## ## ## ## \n",
"##############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"# Starting the process with the chart as |Gantt| and create all other tabs based on the Gantt file \n",
"#***************************************************************************************************************************\n",
"def handle_error(error_message):\n",
" root = tk.Tk()\n",
" root.withdraw()\n",
" messagebox.showerror(\"Error\", error_message)\n",
" root.destroy()\n",
" \n",
"Path = 'Inputs' # Source folder where the file to copy resides\n",
"PathTemp = '2_Temp-file' # Temporary folder for intermediate files\n",
"\n",
"try:\n",
" # Initialize Tkinter root\n",
" root = tk.Tk()\n",
" root.withdraw()\n",
"\n",
" # Pre-fill today's date\n",
" default_date = datetime.now().strftime('%m-%d-%Y')\n",
" file_date_inventory = simpledialog.askstring(\"Inputs date\", \"Enter the date of your inputs (Inventory, Backlog, TurnoverReport) with format MM-DD-YYYY (Ex: 07-03-2024):\", parent=root, initialvalue=default_date)\n",
" \n",
" if not file_date_inventory:\n",
" raise ValueError(\"Date input is required.\")\n",
" \n",
" # Validate date format using regular expressions\n",
" if not re.match(r'\\d{2}-\\d{2}-\\d{4}', file_date_inventory):\n",
" raise ValueError(\"Invalid date format. Please enter the date in MM-DD-YYYY format (Ex: 07-03-2024).\")\n",
" \n",
" print(\"Inventory file Date:\", file_date_inventory)\n",
" \n",
" # Define paths and file names - New files created \n",
" source_file = 'CM_Gantt.xlsx'\n",
" original_input = f'CM-Transfer_Project-Overview_{file_date_inventory}.xlsx'\n",
" input_file_filtered_name = f'Clear-to-Build-{file_date_inventory}_Filtered.xlsx'\n",
" input_file_filtered = os.path.join(PathTemp, input_file_filtered_name)\n",
" \n",
" # Define the input file names - Existings files in 'Inputs'\n",
" input_file_name_CM_BOM = os.path.join(Path,'CM_BOM-QAD_Formatted.xlsx') \n",
" input_file_name_Inventory = os.path.join(Path, f'CM_IDD_Inventory-{file_date_inventory}_Formatted.xlsx')\n",
" \n",
" #Check if the Inventory source file exists\n",
" if not os.path.isfile(input_file_name_Inventory):\n",
" messagebox.showwarning(\"File Not Found\", f\"Filtered file '{input_file_name_Inventory}' does not exist. Please check the entered date format.\")\n",
" raise ValueError(f\"Filtered file '{input_file_name_Inventory}' does not exist. Please check the entered date format.\")\n",
" \n",
" # Check if the source file exists\n",
" if not os.path.isfile(os.path.join(Path, source_file)):\n",
" raise FileNotFoundError(f\"Source file '{source_file}' not found in '{Path}'.\")\n",
"\n",
" # Create the destination folder if it doesn't exist\n",
" os.makedirs(Path, exist_ok=True)\n",
" # Copy the file to the current working directory with the desired name\n",
" copied_file = os.path.join(os.getcwd(), original_input)\n",
" shutil.copy(os.path.join(Path, source_file), copied_file)\n",
"\n",
" #print(f\"File '{source_file}' copied successfully as '{original_input}'.\")\n",
"\n",
" # Load the copied workbook and remove external links\n",
" with warnings.catch_warnings():\n",
" warnings.simplefilter(\"ignore\")\n",
" wb = load_workbook(copied_file, keep_links=False)\n",
" \n",
" # Rename the first sheet to 'Gantt'\n",
" sheet = wb.worksheets[0] # Assuming the first sheet needs to be renamed\n",
" sheet.title = 'Gantt'\n",
" \n",
" # Save the workbook\n",
" wb.save(copied_file)\n",
" wb.close()\n",
"\n",
" # Verify the workbook state (optional, for debugging)\n",
" print(f\"Workbook state after save and close: {wb.sheetnames}\")\n",
" \n",
" print('Initialisation of the Transfer Project Overview spreadsheet ... Processing ...')\n",
" #print(f\"Sheet 1 renamed to 'Gantt' successfully in copied file.\")\n",
"\n",
"#********************************************************************************************************************************************\n",
"#********************************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ########## ############## #########\n",
"## ## ## ## ##\n",
"## ## ## #########\n",
"## ## ## ## ##\n",
"## ########## LEAR ## O ######### UILD\n",
"##############################################################################################################################\n",
"#********************************************************************************************************************************************\n",
"#********************************************************************************************************************************************\n",
" # Load the Excel souce files into DataFrames\n",
" Inventory = pd.read_excel(input_file_name_Inventory, sheet_name=0)\n",
" CM_BOM = pd.read_excel(input_file_name_CM_BOM, sheet_name=0)\n",
"\n",
" #Update 08/28 to make it case incensitive \n",
" # Filter and merge data\n",
" filtered_inventory = Inventory[Inventory['Site'] == 100]\n",
" # Make 'M/P/F' column case-insensitive\n",
" filtered_cm_bom = CM_BOM[\n",
" (CM_BOM['M/P/F'].str.upper() == 'P') | \n",
" (CM_BOM['M/P/F'].str.upper() == 'M')\n",
" ]\n",
" # Merge the filtered data\n",
" merged_data = pd.merge(filtered_inventory, filtered_cm_bom, how='inner', on=['IDD Component', 'IDD Top Level'])\n",
" \n",
" # Write data to 'Clear-to-Build' tabs in input_file_filtered\n",
" with pd.ExcelWriter(input_file_filtered, engine='openpyxl') as writer:\n",
" merged_data.to_excel(writer, sheet_name='Clear-to-Build', index=False)\n",
" Inventory.to_excel(writer, sheet_name='CM-Inventory', index=False)\n",
" CM_BOM.to_excel(writer, sheet_name='CM-BOM', index=False)\n",
"\n",
" #print(f\"3 tabs added to {input_file_filtered}\")\n",
"\n",
" # Load the workbook again for further manipulation\n",
" workbook = load_workbook(input_file_filtered)\n",
"\n",
" # Drop specified columns in 'Clear-to-Build' tab in input_file_filtered\n",
" columns_to_drop = ['O', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AA'] # drop + phantom\n",
" ws_clear_to_build = workbook['Clear-to-Build']\n",
"\n",
" for col_letter in reversed(columns_to_drop):\n",
" col_index = openpyxl.utils.cell.column_index_from_string(col_letter)\n",
" ws_clear_to_build.delete_cols(col_index)\n",
"\n",
" # Save the workbook with the modifications\n",
" workbook.save(input_file_filtered)\n",
" workbook.close()\n",
" #print(\"Columns dropped in |Clear-to-Build| tab.\")\n",
" \n",
" # Reorder and rename columns in 'Clear-to-Build' tab\n",
" ws_clear_to_build_df = pd.read_excel(input_file_filtered, sheet_name='Clear-to-Build')\n",
"\n",
" # Define the new column order and names\n",
" columns_order = ['Priority_x', 'Pty Indice_x', 'Inventory Status', 'Site', 'Pur/Mfg', 'Description',\n",
" 'Qty On Hand', 'Unit cost', 'Cost Total', 'IDD Component', 'Level_x', 'BOM Qty_x',\n",
" 'IDD Top Level', 'SEDA Top Level_x', 'Critical Qty', 'Shipped', 'Remain. crit. Qty',\n",
" 'BOM Index', 'Last update']\n",
"\n",
" # Reorder the columns in the DataFrame\n",
" ws_clear_to_build_df = ws_clear_to_build_df[columns_order]\n",
"\n",
" # Rename the columns in the DataFrame\n",
" new_column_names = ['Priority', 'Pty Indice', 'Inventory Status', 'Site', 'Pur/Mfg', 'Description',\n",
" 'Qty On Hand', 'Unit cost', 'Cost Total', 'IDD Component', 'Level', 'BOM Qty',\n",
" 'IDD Top Level', 'SEDA Top Level', 'Critical Qty', 'Shipped', 'Remain. crit. Qty',\n",
" 'BOM Index', 'Last update']\n",
" ws_clear_to_build_df.columns = new_column_names\n",
"\n",
" #display(ws_clear_to_build_df)\n",
" \n",
" # Write the modified DataFrame back to the Excel file\n",
" with pd.ExcelWriter(input_file_filtered, engine='openpyxl') as writer:\n",
" ws_clear_to_build_df.to_excel(writer, sheet_name='Clear-to-Build', index=False)\n",
"\n",
" #print(\"Columns reordered and renamed in |Clear-to-Build| tab.\")\n",
"\n",
" # Append modified 'Clear-to-Build' tab and 'CM-Inventory' and 'CM-BOM' tabs to original_input\n",
" with pd.ExcelWriter(original_input, engine='openpyxl', mode='a') as writer:\n",
" ws_clear_to_build_df.to_excel(writer, sheet_name='Clear-to-Build', index=False)\n",
" Inventory.to_excel(writer, sheet_name='CM-Inventory', index=False)\n",
" CM_BOM.to_excel(writer, sheet_name='CM-BOM', index=False)\n",
" \n",
" #print(f\"Modified |Clear-to-Build|, |CM-Inventory| and |CM-BOM| saved to '{original_input}'.\")\n",
" print(f\" |CM-Inventory| & |CM-BOM| saved to '{original_input}'.\")\n",
"\n",
" #Close workbook\n",
" #workbook.close() # closing workbook related to input_file_filtered\n",
" \n",
" # Load the workbook to see the sheet names\n",
" #workbook = load_workbook(original_input)\n",
" # Print the sheet names\n",
" #print(\"Sheet names in the workbook:\")\n",
" #print(workbook.sheetnames)\n",
" print(\"Processing |Clear-to-Build| ...\")\n",
"\n",
"except FileNotFoundError as e:\n",
" print(f\"File not found: {e}\")\n",
"\n",
"except ValueError as e:\n",
" print(f\"Value error: {e}\")\n",
"\n",
"except Exception as e:\n",
" print(f\"An unexpected error occurred: {e}\")\n",
" \n",
"#********************************************************************************************************************************************\n",
"#********************************************************************************************************************************************\n",
"## ------------------------------>>>> CREATE CLear-to-build FORMATED\n",
"#********************************************************************************************************************************************\n",
"#********************************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Looad Priority and floorstock\n",
"##############################################################################################################################\n",
"# Load the existing output workbook #New code 07/05\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"'''\n",
"# Get the active sheet\n",
"active_sheet = workbook.active\n",
"\n",
"# Determine the name of the active sheet\n",
"active_sheet_name = None\n",
"for sheet_name in workbook.sheetnames:\n",
" if workbook[sheet_name] == active_sheet:\n",
" active_sheet_name = sheet_name\n",
" break\n",
"\n",
"# Print the active sheet name\n",
"if active_sheet_name:\n",
" print(f\"Active sheet: {active_sheet_name}\")\n",
"else:\n",
" print(\"No active sheet selected (workbook.active = None)\")\n",
"'''\n",
"\n",
"priority_file_name = os.path.join(Path, 'CM_Priority_Database.xlsx')\n",
"Floorstock_input = os.path.join(Path,'Floor-Stock-Items.xlsx')\n",
"\n",
"##############################################################################################################################\n",
"######################### Copying filtered file into original input while keeping first tab file #####################\n",
"###########################################################################################################################\n",
"# Load the Excel file into pandas dataframes\n",
"try:\n",
" df_CM_BOM = pd.read_excel(original_input, sheet_name='CM-BOM')\n",
" df_CTB_updated = pd.read_excel(original_input, sheet_name='Clear-to-Build')\n",
" df_CTB = pd.read_excel(input_file_filtered, sheet_name='Clear-to-Build')\n",
" df_Priority = pd.read_excel(priority_file_name, sheet_name='CM-Priority')\n",
" #print(\"Input files loaded successfully.\")\n",
"except FileNotFoundError:\n",
" print(\"File not found. Please check the file path or name.\")\n",
" exit()\n",
"###########################################################################################################################\n",
"###########################################################################################################################\n",
"#Update 09/09\n",
"# Convert ['Remain. crit. Qty'] from df_Priority as integer as the file 'CM_Priority_Database.xlsx' contain a formulla instead of a number that is not properlly copied in CM-Priority \n",
"df_Priority['Remain. crit. Qty'] = df_Priority['Remain. crit. Qty'].astype(int)\n",
"\n",
"# Print the 'Remain. crit. Qty' column\n",
"#print(df_Priority['Remain. crit. Qty'])\n",
"\n",
"###################################### \n",
"#### Tab Clear-to-Build ###\n",
"###################################### \n",
"################################################################################################################################\n",
"### Exploiting tab clear-to-build \n",
"###############################################################################################################################\n",
"#######Filtering the tab 'Clear-to-Build' to keep a single 'IDD Component' in [J] to avoid duplicate and faciliate the shortage report keeping in priority the component with 'inventory status' to 'GS', then 'R-INSP' then 'MRB' then anything else \n",
"# Define the priority order for inventory statuses\n",
"#New 08/07 --> Some IDD Component has to be duplicated because appear several time on the BOM and are used several time for different Make part --> Sum 'BOM Qty' for these duplicate and keep a single row instead of erasing the row\n",
"###########################################################################################################################\n",
"# New code 08/08\n",
"# Define priority order for inventory statuses\n",
"priority_order = ['GS', 'FG', 'R-INSP', 'MRB', 'RTV', 'GIT2', 'Hold', 'Component not in Inventory']\n",
"\n",
"# Define a function to select the row with the highest priority inventory status for each 'IDD Top Level' and 'IDD Component'\n",
"def select_row_with_priority(group):\n",
" max_priority_row = None\n",
" for _, row in group.iterrows():\n",
" status = row['Inventory Status']\n",
" if pd.notna(status) and status in priority_order:\n",
" if max_priority_row is None or priority_order.index(status) < priority_order.index(max_priority_row['Inventory Status']):\n",
" max_priority_row = row\n",
" return max_priority_row\n",
"\n",
"# Group by 'IDD Top Level' and 'IDD Component' and apply the function to select the row with the highest priority inventory status\n",
"#df_CTB_updated = df_CTB_updated.groupby(['IDD Top Level', 'IDD Component']).apply(select_row_with_priority).reset_index(drop=True) # Replaced 09/24 to avoid warning\n",
"############################# 09/24 update #############################\n",
"# Group by 'IDD Top Level' and 'IDD Component', apply the function\n",
"df_CTB_updated = df_CTB_updated.groupby(['IDD Top Level', 'IDD Component'], as_index=False).apply(select_row_with_priority)\n",
"\n",
"# Reset index to ensure proper formatting (this will keep the grouping columns intact)\n",
"df_CTB_updated = df_CTB_updated.reset_index(drop=True)\n",
"###########################################################################\n",
"\n",
"\n",
"\n",
"##################################\n",
"#Update df_CTB_updated['BOM Qty'] with the sum of 'BOM Qty' from CM_BOM for a combinaison of 'IDD Top Level', 'IDD Component'\n",
"#################################\n",
"# Compute the sum of 'BOM Qty' from CM_BOM for each combination of 'IDD Top Level' and 'IDD Component'\n",
"bom_qty_sum = CM_BOM.groupby(['IDD Top Level', 'IDD Component'])['BOM Qty'].sum().reset_index()\n",
"\n",
"# Merge the summed BOM quantities with df_CTB_updated\n",
"df_CTB_updated = df_CTB_updated.merge(bom_qty_sum, on=['IDD Top Level', 'IDD Component'], how='left', suffixes=('', '_new'))\n",
"\n",
"# Update the existing 'BOM Qty' column with the new values\n",
"df_CTB_updated['BOM Qty'] = df_CTB_updated['BOM Qty_new']\n",
"\n",
"# Drop the temporary 'BOM Qty_new' column\n",
"df_CTB_updated.drop(columns=['BOM Qty_new'], inplace=True)\n",
"\n",
"#########################\n",
"# Load workbook\n",
"#########################\n",
"with pd.ExcelWriter(original_input, engine='openpyxl') as writer:\n",
" # Write the DataFrame df_CTB_updated to the 'Clear-to-Build' tab\n",
" df_CTB_updated.to_excel(writer, sheet_name='Clear-to-Build', index=False)\n",
"\n",
" # Copy other sheets from the input Excel file to the output Excel file\n",
" with pd.ExcelFile(input_file_filtered) as xls:\n",
" for sheet_name in xls.sheet_names:\n",
" if sheet_name != 'Clear-to-Build':\n",
" df = pd.read_excel(xls, sheet_name)\n",
" df.to_excel(writer, sheet_name=sheet_name, index=False)\n",
" \n",
" # Save the updated workbook\n",
" writer.book.save(original_input)\n",
"\n",
"# Update num_rows after saving\n",
"num_rows = df_CTB_updated.shape[0]\n",
"print(\"Number of rows in df_CTB_updated after keeping only unique 'IDD Component' per 'IDD Top Level':\", num_rows)\n",
"\n",
"#############################################################################################################\n",
"#### Define the new columns in DataFrame df_CTB_updated should have the new columns inserted at the end ####\n",
"#############################################################################################################\n",
"new_columns = ['Max Qty (GS)', 'Max Qty Top-Level', 'Cost cancelation impact', 'Top Level sharing Components','Parent Components On Hand']\n",
"#new_columns = ['Max Qty (GS)', 'Max Qty Top-Level', 'Cost cancelation impact', 'Top Level sharing Components']\n",
"\n",
"# Get the number of columns in the DataFrame\n",
"num_columns = len(df_CTB_updated.columns)\n",
"\n",
"# Insert the new columns at the end of the DataFrame\n",
"for column_name in new_columns:\n",
" df_CTB_updated.insert(loc=num_columns, column=column_name, value=None)\n",
" num_columns += 1 # Increment the number of columns\n",
"\n",
"#######################################################################################################################\n",
"############## Include IDD floor stock item in the clear-to-build tab ################\n",
"#######################################################################################################################\n",
"# Load workbook\n",
"with pd.ExcelWriter(original_input, engine='openpyxl') as writer:\n",
" # Write the DataFrame df_CTB_updated to the 'Clear-to-Build' tab\n",
" df_CTB_updated.to_excel(writer, sheet_name='Clear-to-Build', index=False)\n",
"\n",
" # Copy other sheets from the input Excel file to the output Excel file\n",
" with pd.ExcelFile(input_file_filtered) as xls:\n",
" for sheet_name in xls.sheet_names:\n",
" if sheet_name != 'Clear-to-Build':\n",
" df = pd.read_excel(xls, sheet_name)\n",
" df.to_excel(writer, sheet_name=sheet_name, index=False)\n",
" \n",
" # Load the Excel file into pandas dataframes\n",
" df_floorstock = pd.read_excel(Floorstock_input, sheet_name='Floor-Stock')\n",
"\n",
" # Merge df_floorstock with df_CTB_updated on IDD Component to update 'Max Qty (GS)' with 'Floor stock item'\n",
" df_CTB_updated['Max Qty (GS)'] = np.where(df_CTB_updated['IDD Component'].isin(df_floorstock['IDD Component']), 'Floor stock item', df_CTB_updated['Max Qty (GS)'])\n",
"\n",
"###############################################################\n",
"####### Create new columns on the 'Clear-to-Build' tab #########################################################################################\n",
"######[T] Max Qty (GS) - Only considering 'GS' only from [C], while part in [C] = 'R-INSP' are excluded from this calculation and set as 'N/A' in [T] and with [T] Max Qty = Qty on hand [G] / BOM Qty [L]\n",
"# For setting values in 'Max Qty (GS)' column only considering integer as the not integer seems to be 'floor stock'\n",
"####################################################################################################################################################\n",
"'''\n",
" # New 09/23 - Check if 'BOM Qty' is a float and has a decimal part, return 'Floor stock item CUU'\n",
" bom_qty = row['BOM Qty']\n",
" if pd.notnull(bom_qty) and not float(bom_qty).is_integer():\n",
" return 'Floor stock item CUU' # Handles BOM quantities with decimal parts that are not present on the IDD floor-stock file\n",
"'''\n",
"def calculate_max_qty(row):\n",
" # If 'Max Qty (GS)' already contains 'Floor stock item', leave it unchanged\n",
" if row['Max Qty (GS)'] == 'Floor stock item':\n",
" return 'Floor stock item' # If 'Max Qty (GS)' already contains 'Floor stock item', leave it unchanged\n",
" \n",
" if pd.notnull(row['Qty On Hand']) and pd.notnull(row['BOM Qty']):\n",
" qty_on_hand = row['Qty On Hand']\n",
" bom_qty = row['BOM Qty']\n",
" \n",
" # Check if 'BOM Qty' is zero or float \n",
" if bom_qty == 0:\n",
" return 'Floor stock item'\n",
" \n",
" # Check if both 'Qty On Hand' and 'BOM Qty' are integers or have a .0 decimal part\n",
" elif qty_on_hand == int(qty_on_hand) and bom_qty == int(bom_qty):\n",
" # Check if 'Inventory Status' is 'GS' or 'FG'\n",
" if row['Inventory Status'] in ['GS', 'FG']:\n",
" return int(qty_on_hand // bom_qty)\n",
" else:\n",
" return 0 # If 'Inventory Status' is not 'GS' or 'FG', return 0 -- \n",
" else:\n",
" return 'Floor stock item' # Return None if either 'Qty On Hand' or 'BOM Qty' is not integer\n",
" else:\n",
" return 'Floor stock item' # Return None if either 'Qty On Hand' or 'BOM Qty' is null\n",
"\n",
"# Apply the function to calculate values for the 'Max Qty (GS)' \n",
"df_CTB_updated['Max Qty (GS)'] = df_CTB_updated.apply(calculate_max_qty, axis=1)\n",
"\n",
" # Print a message indicating the completion of the process\n",
"print('Max Qty (GS) filled.')\n",
"\n",
"########################################################################################################################################################\n",
"###[W] Shared Components - Listing of the Top-Level using the component to managed the potential shared component by listing in [W] all the top level from [M] using this 'IDD component' from [J]\n",
"# Define a function to list shared components #########################################################################################################\n",
"##########################################################\n",
"#08/09 - Keep the code to avoid changing the order of column \n",
"# Define the function to list shared components\n",
"def list_shared_components(row, df):\n",
" component = row['IDD Component']\n",
" # Filter the DataFrame using boolean indexing\n",
" filtered_df = df[df['IDD Component'] == component]\n",
" # Select the desired columns\n",
" top_levels = filtered_df[['IDD Top Level', 'Pty Indice']]\n",
" # Drop duplicates\n",
" top_levels.drop_duplicates(inplace=True)\n",
" # Check if top_levels is empty\n",
" if not top_levels.empty:\n",
" # Return all shared top levels as a list\n",
" #return list(top_levels.apply(lambda x: f\"{x['IDD Top Level']} [{x['Pty Indice']}]\", axis=1))\n",
" return list(top_levels.apply(lambda x: f\"{x['Pty Indice']}\", axis=1))\n",
" return None\n",
"\n",
"# Assuming df_CTB_Updated is already defined and loaded\n",
"# Apply the function to create 'Top Level sharing Components' column using a lambda function\n",
"df_CTB_updated['Top Level sharing Components'] = df_CTB_updated.apply(lambda row: list_shared_components(row, df_CTB_updated), axis=1)\n",
"\n",
"# Now we have lists of shared components, let's concatenate them into a single string separated by ;\n",
"df_CTB_updated['Top Level sharing Components'] = df_CTB_updated['Top Level sharing Components'].apply(lambda x: '; '.join(x) if x is not None else None)\n",
"\n",
"# Save the updated Clear-to-Build tab to an Excel file\n",
"df_CTB_updated.to_excel(original_input, sheet_name='Clear-to-Build', index=False)\n",
"\n",
"# Print a message indicating the completion of the process\n",
"print(\"Top Level sharing Components filled and saved to\", original_input)\n",
"\n",
"########################################################################################################################################################\n",
"###[V] Cost impact if cancelation - Calculate the impact for IDD of the the cancelation of an order by calculating the total cost of the inventory on hand that won't be used if Top Level is canceled \n",
"# Define a function to calculate 'Cost cancelaation impact' \n",
"##########################################################\n",
"# Calculation should be IF ('Remaining critical Qty' < 'Qty On Hand', 'Remaining critical Qty' * 'Unit cost', 'Qty On Hand' * 'Unit cost')\n",
"# Define a function to calculate 'Cost impact if cancellation'\n",
"\n",
"# Define a function to calculate 'Cost impact if cancellation'\n",
"def calculate_cancellation_impact(row):\n",
" remaining_critical_qty = pd.to_numeric(row['Remain. crit. Qty'], errors='coerce')\n",
" qty_on_hand = pd.to_numeric(row['Qty On Hand'], errors='coerce')\n",
" unit_cost = pd.to_numeric(row['Unit cost'], errors='coerce')\n",
" \n",
" if pd.notnull(remaining_critical_qty) and pd.notnull(qty_on_hand) and pd.notnull(unit_cost):\n",
" if remaining_critical_qty < qty_on_hand:\n",
" return remaining_critical_qty * unit_cost\n",
" else:\n",
" return qty_on_hand * unit_cost\n",
" else:\n",
" return 'N/A' # Return 'N/A' if any of the required columns have missing or non-numeric values\n",
"\n",
"# Apply the function to create the 'Cost impact if cancellation' [V] column\n",
"df_CTB_updated['Cost cancelation impact'] = df_CTB_updated.apply(calculate_cancellation_impact, axis=1)\n",
"\n",
"# Print a message indicating the completion of the process\n",
"print('Cost cancelation impact filled.')\n",
"\n",
"#######################################################################################################################\n",
"############## Include missing component to tab Clear-to-Build tab from tab CM-BOM ################\n",
"#######################################################################################################################\n",
"### This section should include the 'IDD Component' from tab 'CM-BOM' with column 'M/P/F' = 'P' missing in tab 'Clear-to-Build' for a given 'IDD Top Level' \n",
"## Additionnaly, considering tab CM-BOM, for a given 'IDD Top Level', we need to excluded for this list of missing component, the 'IDD Component' which has a upper 'Level' with 'M/F/P' = 'P'\n",
"# For example: 'IDD Component' 100-230909-003 is 'Level' = 4 but for a given 'IDD Top Level', there is a 340-000811-001 with 'Level' = 3 and 'M/P/F' = P menaning that it is a purchase part so 100-230909-003 should be exluded \n",
"# --> We need to ensure that if a lower-level component is marked as 'M', then all its parent components up to the root level should be included in the missing components list\n",
"\n",
"# Define a mapping dictionary to map column names from CM-BOM to Clear-to-Build\n",
"column_mapping = {\n",
" 'Priority':'Priority',\n",
" 'Pty Indice': 'Pty Indice',\n",
" 'Inventory Status': 'Inventory Status', # Add the 'Inventory Status' column mapping\n",
" 'Site':'Site', # Add the 'Site' column mapping\n",
" 'M/P/F':'Pur/Mfg', \n",
" 'Description Component':'Description',\n",
" 'Qty On Hand':'Qty On Hand', # Add the 'Qty on Hand' column mapping\n",
" 'Unit cost':'Unit cost', # Add the 'Site' column mapping\n",
" 'Cost Total':'Cost Total', # Add the 'Cost Total' column mapping\n",
" 'IDD Component': 'IDD Component',\n",
" 'Level': 'Level',\n",
" 'BOM Qty':'BOM Qty',\n",
" 'IDD Top Level':'IDD Top Level',\n",
" 'SEDA Top Level':'SEDA Top Level',\n",
" 'Critical Qty':'Critical Qty', # Add the 'Critical Qty' column mapping\n",
" 'Shipped':'Shipped', # Add the 'Shipped' column mapping\n",
" 'Remain. crit. Qty':'Remain. crit. Qty', # Add the 'Remaining critical Qty' column mapping\n",
" 'BOM Index':'BOM Index',\n",
" 'Last update':'Last update' # Add the 'Last Update' column mapping\n",
"}\n",
"\n",
"#***********************************************\n",
"# ---->>> INCLUDE MAKE PART MISSING in addition to P parts\n",
"#*************************************************\n",
"# Identify missing components with 'M/P/F' = 'P'\n",
"#missing_components = df_CM_BOM[(df_CM_BOM['M/P/F'] == 'P') & (~df_CM_BOM['IDD Component'].isin(df_CTB_updated['IDD Component']))]\n",
"#missing_components = df_CM_BOM[((df_CM_BOM['M/P/F'] == 'P') | (df_CM_BOM['M/P/F'] == 'M')) & (~df_CM_BOM['IDD Component'].isin(df_CTB_updated['IDD Component']))] \n",
"\n",
"#Update 08/28 to include case incencive \n",
"# Identify missing components with 'M/P/F' = 'P' or 'M' (case insensitive), excluding Level 0 components\n",
"missing_components = df_CM_BOM[\n",
" ((df_CM_BOM['M/P/F'].str.upper() == 'P') | (df_CM_BOM['M/P/F'].str.upper() == 'M')) & \n",
" (~df_CM_BOM['IDD Component'].isin(df_CTB_updated['IDD Component'])) & \n",
" (df_CM_BOM['Level'] != 0)\n",
"]\n",
"\n",
"# Get the IDs of missing components\n",
"missing_component_ids = missing_components['IDD Component'].tolist()\n",
"\n",
"# Print the missing components\n",
"num_missing_components = len(missing_component_ids)\n",
"print('Number of missing components:', num_missing_components)\n",
"\n",
"# Display the missing components dataframe\n",
"#print(\"Missing components from CM-BOM:\")\n",
"#display(missing_components)\n",
"\n",
"# Append missing rows to df_CTB_updated with correct column values\n",
"new_rows = []\n",
"for index, row in missing_components.iterrows():\n",
" new_row = {}\n",
" for col_cm_bom, col_ctb in column_mapping.items():\n",
" if col_cm_bom in df_CM_BOM.columns:\n",
" new_row[col_ctb] = row[col_cm_bom]\n",
" else:\n",
" # Set default value for missing columns\n",
" if col_ctb == 'Inventory Status':\n",
" new_row[col_ctb] = 'Component not in Inventory'\n",
" elif col_ctb == 'Qty On Hand':\n",
" new_row[col_ctb] = 0\n",
" else:\n",
" # Set default value based on your requirement\n",
" new_row[col_ctb] = None # You can replace None with appropriate default values if needed\n",
" new_rows.append(new_row)\n",
"\n",
"# Convert the list of dictionaries to a DataFrame\n",
"new_rows_df = pd.DataFrame(new_rows)\n",
"\n",
"# Get the updated number of rows in df_CTB_updated\n",
"num_rows = df_CTB_updated.shape[0]\n",
"print(\"Number of rows in |Clear-to-Build| before including missing component:\", num_rows)\n",
"\n",
"# Concatenate the new rows DataFrame with df_CTB_updated\n",
"# df_CTB_updated = pd.concat([df_CTB_updated, new_rows_df], ignore_index=True) # Updated 09/24 to avoid warning\n",
"\n",
"##### update 09/24 ##################\n",
"# Ensure no empty or all-NA columns in new_rows_df\n",
"new_rows_df = new_rows_df.dropna(axis=1, how='all')\n",
"# Concatenate the DataFrames\n",
"df_CTB_updated = pd.concat([df_CTB_updated, new_rows_df], ignore_index=True)\n",
"####################################\n",
"\n",
"# Get the updated number of rows in df_CTB_updated\n",
"num_rows = df_CTB_updated.shape[0]\n",
"\n",
"# Print the shape of the DataFrame after appending missing components\n",
"print(\"Number of rows in |Clear-to-Build| after appending missing components - Including Make Part:\", df_CTB_updated.shape[0])\n",
"\n",
"#########################################################################################\n",
"# Save the updated Clear-to-Build tab\n",
"try:\n",
" with pd.ExcelWriter(original_input, engine='openpyxl') as writer:\n",
" df_CTB_updated.to_excel(writer, sheet_name='Clear-to-Build', index=False)\n",
" print('Missing components from |CM-BOM| have been included in |Clear-to-Build|.')\n",
"except PermissionError:\n",
" print(\"Permission denied. Please make sure the file is not open and try again.\")\n",
" exit()\n",
"\n",
"# Save the updated workbook\n",
"workbook.save(original_input)\n",
"workbook.close()\n",
"\n",
"#################################################################\n",
"### Based on BOM Index reordering \n",
"##############################################################\n",
"# Reordering 'Clear-to-Build' based on BOM Index and IDD Top level\n",
"df_CTB_updated = df_CTB_updated.sort_values(by=['IDD Top Level', 'BOM Index'])\n",
"\n",
"##############################################\n",
"# [T] Max Qty (GS) to N/A if BOM Qty = 0\n",
"###############################################\n",
"# Update 'Max Qty (GS)' to 'N/A' if 'BOM Qty' is 0, only if 'Max Qty (GS)' is NaN\n",
"condition = (df_CTB_updated['BOM Qty'] == 0) & (df_CTB_updated['Max Qty (GS)'].isna())\n",
"\n",
"df_CTB_updated.loc[condition, 'Max Qty (GS)'] = 'N/A'\n",
"\n",
"####################################################################\n",
"## [V] Cost Cancelation impact for 'Component not in inventory' to N/A \n",
"## #################################################################\n",
"# Update 'Cost cancelation impact' to 'N/A' if 'Inventory Status' is 'Component not in Inventory'\n",
"# but retain 'Floor stock item' where it already exists\n",
"condition = (df_CTB_updated['Inventory Status'] == 'Component not in Inventory') & \\\n",
" (df_CTB_updated['Cost cancelation impact'] != 'Floor stock item')\n",
"\n",
"df_CTB_updated.loc[condition, 'Cost cancelation impact'] = 'N/A'\n",
"\n",
"###############################################################\n",
"####### Create new columns on the 'Clear-to-Build' tab #########################################################################################\n",
"######[X] Parent component On Hand' - In order to define if we are clear to build, for a given component we need to consider the potential 'M' component which is a parent component to a 'P' component (upper Level) that we might have in 'GS' \n",
"#### The 'Max Qty Top-Level' should include the 'M' parent component on hand instead of the lower level component if > to the 'Max Qty (GS) of this given component \n",
"#### In order to find the Qty of 'M' parent component in 'GS' we need to use the tab 'CM-Iventory' to get the Qty on hand, combined with 'CM-BOM' to get the architecture from\n",
"####################################################################################################################################################\n",
"#Sorting: Ensure the DataFrame is sorted by 'Pty Indice' and 'BOM Index' for proper sequential processing.\n",
"#Filtering by Pty Indice: Iterate over each unique 'Pty Indice' and filter the DataFrame for the selected 'Pty Indice'.\n",
"#Sub-group Creation: Within each 'Pty Indice', detect sub-groups by identifying 'Level 1' components and their ranges.\n",
"#Parent Component Detection: For each row in the sub-group, find parent components within the same sub-group that have a lower 'Level' and a non-zero 'Qty On Hand'.\n",
"###########################################################################\n",
"###################### REQUIREMENTS ######################################\n",
"###########################################################################\n",
"'''\n",
"This code defines a function called process_subgroup and then iterates over each group in the DataFrame df_CTB_updated, where the groups are defined based on the 'Pty Indice' column. For each group, it performs the following steps:\n",
"1. Initializes variables to keep track of the previous parent component information, the start index of the subgroup, and iterates over each row in the group DataFrame.\n",
"2. Checks if the current row is a Level 1 component. If it is, it processes the previous subgroup (if any) using the process_subgroup function and updates the start index to the current index.\n",
"3. Updates the previous parent component information.\n",
"4. Processes the last subgroup in the group by calling the process_subgroup function with the start index, end index (maximum index in the group), and previous parent component information.\n",
"\n",
"The process_subgroup function takes a DataFrame, start index, end index, and previous parent component information as input. Within this function, it:\n",
"1. Prints a message indicating the start and end indices of the subgroup being processed.\n",
"2. Obtains the subgroup DataFrame based on the start and end indices.\n",
"3. Iterates over each row in the subgroup and checks if the 'BOM Index' is not NaN.\n",
"4. Finds parent components within the subgroup by filtering the DataFrame based on conditions such as the index being less than the current index, the 'Level' being lower than the current row's level, and the 'Qty On Hand' being greater than 0.\n",
"5. If parent components are found, it updates the 'Parent Components On Hand' column in the original DataFrame with information about the parent component(s) found.\n",
"--> Overall, this code aims to find parent components for each row in the DataFrame within subgroups defined by the Level 1 components and update the 'Parent Components On Hand' column accordingly.\n",
"\n",
"The subgroup creation logic is implemented within the main loop iterating over each group in the DataFrame based on the 'Pty Indice' column. Here's how the subgroup creation works within the code:\n",
"1. The iteration begins over each row in the group DataFrame.\n",
"2. When encountering a Level 1 component, it starts a new subgroup. The start index of the subgroup is updated to the index of the Level 1 component.\n",
"3. For each row after the start of a subgroup and before the next Level 1 component, it processes the previous subgroup.\n",
"4. The end index of the subgroup is set to the maximum index in the group DataFrame when reaching the next Level 1 component or the end of the DataFrame.\n",
"5 The process_subgroup function is called for each subgroup, passing the start index, end index, and previous parent component information.\n",
"6. The process_subgroup function processes each row within the subgroup to find and update the parent component information.\n",
"--> Overall, the subgroup creation is implicitly defined by the logic of starting a new subgroup when encountering a Level 1 component and ending it before the next Level 1 component. This ensures that each subgroup contains all the rows between two consecutive Level 1 components.\n",
"\n",
"The issue might be within the process_subgroup function, where it searches for and updates the parent component information. Let's break down what the function does:\n",
"1. Subgroup Selection: It takes the start and end indices to define the subgroup within the DataFrame.\n",
"2. Iteration Over Rows: It iterates over each row within the subgroup.\n",
"3. Parent Component Search:\n",
"- For each row, it checks if the 'BOM Index' is not NaN, indicating a component.\n",
" -- Then, it filters the DataFrame to find potential parent components within the same subgroup:\n",
" -- Rows with an index less than the current row's index.\n",
" -- Rows with a 'Level' lower than the current row's 'Level'.\n",
" -- Rows with a positive 'Qty On Hand'.\n",
"- If parent components are found, it selects the one with the smallest 'Level' (Level 1 over Level 2 etc). The function selects the parent component with the highest 'Level' (= lowest value) to ensure it's the closest parent to the current component in the hierarchy) and updates the 'Parent Components On Hand' column for the current row.\n",
"\n",
"\n",
"Restrict the search for parent components within the same subgroup only, which means we should not consider Level 1 components from other subgroups as potential parent components\n",
"--> Ensures that each subgroup contains ONLY ONE Level 1 -- strictly limit the search for parent components within the same subgroup, we'll add a condition to reset the start index whenever we encounter a Level 1 component. This will effectively partition the data into subgroups based on Level 1 components and ensure that parent-child relationships are only established within these subgroups\n",
"\n",
"##### UPDATE\n",
"1. Wihtin the existing sub-group function (based on level == 1), create a new function sub-subgroup to define the Make architecture of this sub-group (starts with a M part and finish at the last 'P' part before the next 'M' part)\n",
"2. A parent component is necessary a 'M' part of a child which is also a 'M' Part \n",
"3. A parent component is necessary a 'P' part of a child which is a 'P' Part AND within the same sub-subgroup \n",
"\n",
"##### UPDATE 2\n",
"1. Phantomed component ['Phantom'] = 'Oui' have to be identified on the BOM and the row skiped from the search as it will never be in GS but remaing important to define the sub-subgroup function\n",
"2. Wihtin the existing sub-group function (based on level == 1), create a new function sub-subgroup to define the Make architecture of this sub-group (starts with a M part and finish at the last 'P' part before the next 'M' part)\n",
" a. A parent component is necessary a 'M' part of a child which is also a 'M' Part \n",
" b a 'phantom' part \n",
" a. A 'P' part always goes on a 'M' Part with higher level (Level = 3 goes in level = 2 or 1)\n",
" b. A 'P' parts can be parent only of a 'M' part with a higher level within the same sub-subgroup \n",
"\n",
"'''\n",
"# Additional requirements:\n",
"# 09/20 - Pur/Mfg == 'D' mean that this is a Make Part build at CUU --> D should be treated as a M part meaning that if we have some stock of a 'D' part we don't need the lower level because transfered from CUU to RED.\n",
"# If 'Inventory Status' = 'Component not in Inventory' and the there is a row containing 'Pur/Mfg' = 'D' within the subgroupd at a upper level of a given 'IDD Component' then this component should be return in the 'Parent Components On Hand' column \n",
"## Datafram Example: 100-400798-002 should be return as a 'Parent component' for 100-400798-002\n",
"# Pty Indice\tInventory Status\tSite\tPur/Mfg\tDescription\tQty On Hand\tUnit cost\tCost Total\tIDD Component\tLevel\n",
"# P17A\tGS\t100\tD\tCIRCUIT BOARD\t335\t2.927483334\t980.7069168\t100-400798-002\t2\n",
"# P17A\tComponent not in Inventory\t\tP\t.031 FIBERGLASS SHEET\t0\t\t\t100-400753-001\t3\n",
"\n",
"###########################################################################\n",
"###################### CODE WIP ######################################\n",
"###########################################################################\n",
"# Remove duplicates based on relevant columns\n",
"df_CTB_updated.drop_duplicates(subset=['Pty Indice', 'BOM Index', 'Level', 'IDD Component', 'Qty On Hand'], keep='first', inplace=True)\n",
"\n",
"# Create a new column 'Parent Components On Hand' with default value \"No parent component on hand\"\n",
"df_CTB_updated['Parent Components On Hand'] = \"No parent component on hand\"\n",
"\n",
"# Sort the DataFrame by 'Pty Indice' and 'BOM Index' to ensure proper processing\n",
"df_CTB_updated.sort_values(by=['Pty Indice', 'BOM Index'], inplace=True)\n",
"\n",
"# Function to identify and process sub-subgroups\n",
"def identify_subsubgroup(subgroup_df):\n",
" subsubgroups = []\n",
" current_subsubgroup = []\n",
" m_part_found = False\n",
" \n",
" for index, row in subgroup_df.iterrows():\n",
" if row['Level'] == 1:\n",
" if m_part_found and current_subsubgroup:\n",
" subsubgroups.append(current_subsubgroup)\n",
" current_subsubgroup = []\n",
" current_subsubgroup.append(index)\n",
" m_part_found = True\n",
" elif m_part_found and row['Level'] == 2:\n",
" current_subsubgroup.append(index)\n",
" \n",
" # Append the last sub-subgroup\n",
" if current_subsubgroup:\n",
" subsubgroups.append(current_subsubgroup)\n",
" \n",
" return subsubgroups\n",
"\n",
"# Updated 09/03 as upper code was returning an error \n",
"# Function to process each subgroup\n",
"def process_subgroup(df, start_index, end_index):\n",
" # Obtain the subgroup DataFrame\n",
" if isinstance(start_index, int) and isinstance(end_index, int):\n",
" subgroup_df = df.iloc[start_index:end_index+1]\n",
" else:\n",
" print(f\"Invalid indices: {start_index}, {end_index}\")\n",
" return\n",
" \n",
" parent_updates_count = 0 # To store the count of parent component updates\n",
" \n",
" # Check if subgroup DataFrame is not empty and contains exactly one Level 1 component\n",
" if not subgroup_df.empty and subgroup_df['Level'].eq(1).sum() == 1:\n",
" # Get the Pty Indice value for the subgroup\n",
" pty_indice_value = subgroup_df.iloc[0]['Pty Indice']\n",
" \n",
" # Identify and process each sub-subgroup\n",
" subsubgroups = identify_subsubgroup(subgroup_df)\n",
" \n",
" for subsubgroup_indices in subsubgroups:\n",
" # Extract sub-subgroup DataFrame\n",
" subsubgroup_df = df.loc[subsubgroup_indices]\n",
" m_part_found = False\n",
" parent_component_info = None\n",
" \n",
" # Iterate over each row in the sub-subgroup\n",
" for index, row in subsubgroup_df.iterrows():\n",
" if row['Level'] == 1:\n",
" m_part_found = True\n",
" elif m_part_found and row['Level'] == 2:\n",
" # Find parent components that are 'M' parts and closest based on 'BOM Index'\n",
" parent_components_rows = subgroup_df[\n",
" (subgroup_df.index < index) & # Exclude current row\n",
" (subgroup_df['Level'] < row['Level']) &\n",
" (subgroup_df['Qty On Hand'] > 0) & # Filter for components with Qty > 0\n",
" (subgroup_df['Pty Indice'] == pty_indice_value) & # Ensure same Pty Indice\n",
" (subgroup_df['IDD Component'].str.startswith('M')) # Filter for 'M' parts\n",
" ]\n",
" \n",
" # If parent component(s) are found, select the one with closest BOM Index\n",
" if not parent_components_rows.empty:\n",
" closest_parent = parent_components_rows.loc[\n",
" parent_components_rows['BOM Index'].idxmin() # Select the row with the smallest BOM Index\n",
" ]\n",
" if parent_component_info is not None:\n",
" parent_component_info += \", \"\n",
" else:\n",
" parent_component_info = \"\"\n",
" parent_component_info += (\n",
" f\"{closest_parent['IDD Component']} (Level {int(closest_parent['Level'])}): Qty = {int(closest_parent['Qty On Hand'])}\"\n",
" )\n",
" parent_updates_count += 1\n",
" \n",
" # Update 'Parent Components On Hand' column in the original DataFrame\n",
" if parent_component_info:\n",
" df.loc[subsubgroup_df.index, 'Parent Components On Hand'] = parent_component_info\n",
" \n",
" # Summary print statement for the subgroup\n",
" # print(f\"Processed Subgroup from index {start_index} to {end_index} with {parent_updates_count} parent updates.\")\n",
"\n",
"# Group the DataFrame by 'Pty Indice'\n",
"grouped = df_CTB_updated.groupby('Pty Indice')\n",
"\n",
"# Iterate over each group\n",
"for group_name, group_df in grouped:\n",
" # Initialize the start index of the subgroup\n",
" start_index = None\n",
" \n",
" # Iterate over each row in the group\n",
" for index, row in group_df.iterrows():\n",
" # Check if the current row is a Level 1 component and Level is not 0\n",
" if row['Level'] == 1 and row['Level'] != 0:\n",
" # Process the previous subgroup if it exists\n",
" if start_index is not None:\n",
" end_index = index - 1\n",
" process_subgroup(df_CTB_updated, start_index, end_index)\n",
" # Reset the start index for the new subgroup\n",
" start_index = index\n",
" \n",
" # Process the last subgroup if start_index is not None\n",
" if start_index is not None:\n",
" end_index = len(group_df) - 1\n",
" process_subgroup(df_CTB_updated, start_index, end_index)\n",
"\n",
"# Display the updated DataFrame\n",
"#display(df_CTB_updated)\n",
"## End of update 09/03\n",
"\n",
"###########################################################################\n",
"##########################################################################\n",
"## Update Max Qty (GS) base on Parent component (GS) \n",
" ## Read Parent component (GS) on exctrat the higest number, for example:\n",
" ## 100-280013-1 (Level 2): Qty = 6, 100-280113-1 (Level 2): Qty = 13, 100-280195-1 (Level 1): Qty = 7 --> extract 7 \n",
" ## Fill Max Qty (GS) with the current value + the extracting value \n",
"########################################################\n",
"# Initialize a dictionary to store the maximum Qty On Hand for each parent component\n",
"max_qty_dict = {}\n",
"\n",
"# Iterate over each row in df_CTB_updated\n",
"for _, row in df_CTB_updated.iterrows():\n",
" # Extract the parent components on hand from the 'Parent Components On Hand' column\n",
" parent_components_on_hand = row['Parent Components On Hand'].split(', ')\n",
" \n",
" # Iterate over each parent component\n",
" for parent_component_info in parent_components_on_hand:\n",
" # Split the parent component info to extract quantity and unit\n",
" parent_info_split = parent_component_info.split('=')\n",
" if len(parent_info_split) >= 2:\n",
" parent_qty_info = parent_info_split[1].strip().split()\n",
" if len(parent_qty_info) >= 2:\n",
" parent_qty_on_hand = int(parent_qty_info[1])\n",
" \n",
" # Extract the parent component number\n",
" parent_component_number = parent_info_split[0].split('(')[0].strip()\n",
" \n",
" # Update the maximum quantity for the parent component in the max_qty_dict\n",
" if parent_component_number in max_qty_dict:\n",
" max_qty_dict[parent_component_number] = max(max_qty_dict[parent_component_number], parent_qty_on_hand)\n",
" else:\n",
" max_qty_dict[parent_component_number] = parent_qty_on_hand\n",
"\n",
"# Update the 'Max Qty (GS)' column based on the maximum quantity from parent components\n",
"for index, row in df_CTB_updated.iterrows():\n",
" max_qty = row['Max Qty (GS)']\n",
" \n",
" # Extract parent component numbers from the 'Parent Components On Hand' column\n",
" parent_components_on_hand = row['Parent Components On Hand'].split(', ')\n",
" \n",
" # Iterate over each parent component and its maximum quantity\n",
" for parent_component_info in parent_components_on_hand:\n",
" # Split the parent component info to extract quantity and unit\n",
" parent_info_split = parent_component_info.split('=')\n",
" if len(parent_info_split) >= 2:\n",
" parent_qty_info = parent_info_split[1].strip().split()\n",
" if len(parent_qty_info) >= 2:\n",
" parent_component_number = parent_info_split[0].split('(')[0].strip()\n",
" parent_qty_on_hand = max_qty_dict.get(parent_component_number, 0)\n",
" max_qty = max(max_qty, parent_qty_on_hand)\n",
" \n",
" # Update the 'Max Qty (GS)' column with the new maximum quantity\n",
" df_CTB_updated.at[index, 'Max Qty (GS)'] = max_qty\n",
"\n",
"# Print a message indicating the completion of the process\n",
"print('Max Qty (GS) updated.')\n",
"\n",
"############################################################################################\n",
"## Updated Max Qty (GS) with floor stock Itemp to consider 'Component not in Inventory' \n",
"############################################################################################\n",
"# If Inventory Status = 'Component not in Inventory' AND 'IDD Component' part of floor-stock file, then update the Max Qty (GS) with 'Floor stock item'\n",
"#df_floorstock = pd.read_excel('Floor-Stock-Items.xlsx', sheet_name='Floor-Stock')\n",
"\n",
"# Check if 'IDD Component' is in the floor-stock file and 'Inventory Status' is 'Component not in Inventory'\n",
"mask = (df_CTB_updated['IDD Component'].isin(df_floorstock['IDD Component'])) & (df_CTB_updated['Inventory Status'] == 'Component not in Inventory')\n",
"\n",
"# Update 'Max Qty (GS)' with 'Floor stock item' where the condition is met\n",
"df_CTB_updated.loc[mask, 'Max Qty (GS)'] = 'Floor stock item'\n",
"\n",
"#################################################################################################################################################################################\n",
"# Updated 09/23 - Updated Max Qty (GS) with 'Make Part CUU' --> To not considered rows where Pur/Mfg' == 'D' to define 'Max Qty Top-Level' --> Write 'Make Part CUU' on 'Max Qty (GS)' so it won't be considered in df_Summary\n",
"#################################################################################################################################################################################\n",
"# Update 'Max Qty (GS)' with 'Make Part CUU' where 'Pur/Mfg' is 'D'\n",
"df_CTB_updated.loc[df_CTB_updated['Pur/Mfg'] == 'D', 'Max Qty (GS)'] = 'Make Part CUU'\n",
"\n",
"print('Max Qty (GS) updated with Floor stock Item and Make Part from CUU')\n",
"\n",
"##############################################################\n",
"### for a given IDD Top Level put [T] Max (GS) to 'Upper level on hand' if: \n",
" ## 'Inventory status' = 'Component not in Inventory' \n",
" ##********** OR 'Inventory status' = 'GS' & 'Max Qty (GS)' = 0 ************\n",
"##############################################################\n",
"# Iterate over each row in df_CTB_updated --> # Update 09/23 to not consider 'BOM Qty' not integer in the 'Max Qty Top-Level' calculation \n",
"for idx, row in df_CTB_updated.iterrows():\n",
" # Check if 'BOM Qty' is an integer\n",
" if pd.notnull(row['BOM Qty']) and row['BOM Qty'] == int(row['BOM Qty']):\n",
" # Check Upper Levels based on 'Parent Components On Hand' and BOM Qty > 0\n",
" if ((row['Inventory Status'] == 'Component not in Inventory' or \n",
" (row['Inventory Status'] == 'GS' and row['Max Qty (GS)'] == 0)) and \n",
" isinstance(row['Parent Components On Hand'], str) and row['BOM Qty'] > 0):\n",
" if row['Parent Components On Hand'] == 'No parent component on hand':\n",
" if row['Max Qty (GS)'] != 'Floor stock item': # Check if the current value is not 'Floor stock item'\n",
" df_CTB_updated.at[idx, 'Max Qty (GS)'] = 0\n",
" else:\n",
" if row['Max Qty (GS)'] != 'Floor stock item': # Check if the current value is not 'Floor stock item'\n",
" df_CTB_updated.at[idx, 'Max Qty (GS)'] = row['Parent Components On Hand']\n",
"\n",
"\n",
"''' SAVED 09/23\n",
"for idx, row in df_CTB_updated.iterrows():\n",
" # Check Upper Levels based on 'Parent Components On Hand' and BOM Qty > 0 \n",
" if ((row['Inventory Status'] == 'Component not in Inventory' or \n",
" (row['Inventory Status'] == 'GS' and row['Max Qty (GS)'] == 0)) and \n",
" isinstance(row['Parent Components On Hand'], str) and row['BOM Qty'] > 0):\n",
" if row['Parent Components On Hand'] == 'No parent component on hand':\n",
" if row['Max Qty (GS)'] != 'Floor stock item': # Check if the current value is not 'Floor stock item'\n",
" df_CTB_updated.at[idx, 'Max Qty (GS)'] = 0\n",
" else:\n",
" if row['Max Qty (GS)'] != 'Floor stock item': # Check if the current value is not 'Floor stock item'\n",
" df_CTB_updated.at[idx, 'Max Qty (GS)'] = row['Parent Components On Hand']\n",
"'''\n",
"##############################################\n",
"#### [U] Max Qty Top-Level to N/A if Max Qty (GS) = 'N/A, to 0 if Max Qty (GS) = 0 and to 'Make Part' if this is a M part \n",
"## And update Max Qty Top-Level to 0 for Top-Levels where condition_zero is met\n",
"###############################################\n",
"# 09/20 - Pur/Mfg == 'D' mean that this is a Make Part build at CUU --> D should be treated as a M part meaning that if we have some stock of a 'D' part we don't need the lower level because transfered from CUU to RED.\n",
"\n",
"# Initial update of 'Max Qty Top-Level' based on conditions\n",
"df_CTB_updated['Max Qty Top-Level'] = df_CTB_updated['Max Qty (GS)']\n",
"df_CTB_updated.loc[df_CTB_updated['Pur/Mfg'] == 'M', 'Max Qty Top-Level'] = 'Make part'\n",
"df_CTB_updated.loc[df_CTB_updated['Max Qty (GS)'].notna() & df_CTB_updated['Max Qty (GS)'].astype(str).str.contains('Qty =', na=False), 'Max Qty Top-Level'] = 'Upper level on Hand'\n",
"df_CTB_updated.loc[df_CTB_updated['Max Qty (GS)'] == 'Floor stock item', 'Max Qty Top-Level'] = 'N/A'\n",
"\n",
"# Function to extract numeric quantity from 'Max Qty (GS)'\n",
"def extract_numeric_qty(value):\n",
" if isinstance(value, str):\n",
" # Using regular expression to find numeric values directly or 'Qty =' pattern\n",
" match = re.search(r'(\\d+)', value)\n",
" if match:\n",
" return int(match.group()) # Extracting the numeric value found\n",
" return None\n",
"\n",
"# Apply function to create 'Numeric Qty' column, excluding rows where 'Pur/Mfg' == 'M' or non-numeric values - Update 09/23 to exlude 'BOM Qty' none integer\n",
"#df_CTB_updated['Numeric Qty'] = df_CTB_updated.apply(lambda row: extract_numeric_qty(row['Max Qty (GS)']) if row['Pur/Mfg'] != 'M' else np.nan, axis=1)\n",
"df_CTB_updated['Numeric Qty'] = df_CTB_updated.apply(\n",
" lambda row: extract_numeric_qty(row['Max Qty (GS)']) \n",
" if pd.notnull(row['BOM Qty']) and row['BOM Qty'] == int(row['BOM Qty']) \n",
" else np.nan, \n",
" axis=1\n",
")\n",
"\n",
"# Fill 'Numeric Qty' column with numeric values from 'Max Qty (GS)' where 'Max Qty (GS)' is already numeric excluding rows where 'Pur/Mfg' == 'M' or non-numeric values\n",
"numeric_mask = df_CTB_updated['Max Qty (GS)'].apply(lambda x: isinstance(x, (int, float)))\n",
"#df_CTB_updated.loc[numeric_mask & (df_CTB_updated['Pur/Mfg'] != 'M'), 'Numeric Qty'] = df_CTB_updated.loc[numeric_mask & (df_CTB_updated['Pur/Mfg'] != 'M'), 'Max Qty (GS)'] # Update 09/24 to avoid warning\n",
"df_CTB_updated.loc[numeric_mask & (df_CTB_updated['Pur/Mfg'] != 'M'), 'Numeric Qty'] = pd.to_numeric(df_CTB_updated.loc[numeric_mask & (df_CTB_updated['Pur/Mfg'] != 'M'), 'Max Qty (GS)'], errors='coerce')\n",
"\n",
"# Calculate minimum numeric values for each 'Pty Indice'\n",
"min_values = df_CTB_updated[df_CTB_updated['Numeric Qty'].notna()].groupby('Pty Indice')['Numeric Qty'].min().to_dict()\n",
"\n",
"# Update condition 09/23 - Check if 'BOM Qty' is an integer and exclude 'BOM Qty' containing a float \n",
"# Function to update 'Max Qty Top-Level' with minimum values for each 'Pty Indice'\n",
"def update_max_qty_top_level(row):\n",
" if row['Pty Indice'] in min_values:\n",
" return min_values[row['Pty Indice']]\n",
" return row['Max Qty Top-Level']\n",
"\n",
"# Apply the update function to rows with numeric values in 'Numeric Qty'\n",
"df_CTB_updated['Max Qty Top-Level'] = df_CTB_updated.apply(update_max_qty_top_level, axis=1)\n",
"\n",
"#Drop |Numeric Qty|\n",
"df_CTB_updated = df_CTB_updated.drop('Numeric Qty', axis =1) \n",
"\n",
"# Print the updated DataFrame\n",
"print(\"|Clear-to-Build| after updating Max Qty (GS) and handling Pur/Mfg == 'M' condition.\")\n",
"\n",
"##################################################################################################################\n",
"#### Fill Remain. crit. Qty for Inventory Status = 'Component not in Inventory' by checking the 'Pty Indice' #### \n",
"################################################################################################################\n",
"#Update on 08/09 -- Does not work for PN witn ONLY 'Component not in Inventory'\n",
"#Create a mapping dictionary from df_Priority on 'Pty Indice'\n",
"priority_mapping = df_Priority.set_index('Pty Indice')['Remain. crit. Qty'].to_dict()\n",
"\n",
"# Step 2: Define the function to fill 'Remain. crit. Qty' using the mapping\n",
"def fill_remaining_critical_qty(row):\n",
" # Check if 'Remain. crit. Qty' is NaN and 'Inventory Status' is 'Component not in Inventory'\n",
" if pd.isna(row['Remain. crit. Qty']) and row['Inventory Status'] == 'Component not in Inventory':\n",
" # Use the mapping if the 'Pty Indice' exists in the dictionary\n",
" return priority_mapping.get(row['Pty Indice'], row['Remain. crit. Qty'])\n",
" # Return the existing value if the conditions are not met\n",
" return row['Remain. crit. Qty']\n",
"\n",
"# Apply the function to fill remaining critical quantity\n",
"df_CTB_updated['Remain. crit. Qty'] = df_CTB_updated.apply(fill_remaining_critical_qty, axis=1)\n",
"\n",
"# Print a message indicating the completion of the process\n",
"print(\"Remaining critical quantity filled for inventory status 'Component not in Inventory'.\")\n",
"\n",
"#######################################################################################################################\n",
"# New 09/23 - Function to check if 'BOM Qty' is a float with a decimal part, and return 'Floor stock item CUU' if true\n",
"#######################################################################################################################\n",
"def check_bom_qty(row):\n",
" bom_qty = row['BOM Qty']\n",
" \n",
" # Check if 'Inventory Status' is 'Component not in Inventory'\n",
" if row['Inventory Status'] == 'Component not in Inventory':\n",
" # Check if BOM Qty is not null and has a decimal value\n",
" if pd.notnull(bom_qty) and not float(bom_qty).is_integer():\n",
" return 'Floor stock item CUU' # Mark as floor stock CUU if BOM Qty has a decimal part\n",
" \n",
" # Return existing value if conditions are not met\n",
" return row['Max Qty (GS)']\n",
"\n",
"# Apply the function to the DataFrame\n",
"df_CTB_updated['Max Qty (GS)'] = df_CTB_updated.apply(check_bom_qty, axis=1)\n",
"\n",
"# Print a message indicating the completion of the process\n",
"print(\"Floor-stock CUU identified for relevant rows.\")\n",
"\n",
"#########################################################################\n",
"# New code 08/09 -- Code need some work, it is not working\n",
"#########################################################################\n",
"''' TO BE UPDATED \n",
"#################################################################################################################################################\n",
"#### Update 'Top Level sharing Component' for Inventory Status = 'Component not in Inventory' by checking the 'Pty Indice' AND 'IDD Component' #### \n",
"################################################################################################################################################\n",
"def Update_Top_Level_Sharing_Components(df):\n",
" \"\"\"\n",
" Update the 'Top Level sharing Components' column for rows where\n",
" 'Inventory Status' is 'Component not in Inventory'.\n",
" \n",
" Args:\n",
" df (pd.DataFrame): The DataFrame to be updated.\n",
" \n",
" Returns:\n",
" pd.DataFrame: The updated DataFrame.\n",
" \"\"\"\n",
" def list_shared_components(component, df):\n",
" filtered_df = df[df['IDD Component'] == component]\n",
" top_levels = filtered_df[['IDD Top Level', 'Pty Indice']].drop_duplicates()\n",
" return '; '.join(top_levels['Pty Indice']) if not top_levels.empty else None\n",
" \n",
" # Update 'Top Level sharing Components' for rows where Inventory Status is 'Component not in Inventory'\n",
" mask = df['Inventory Status'] == 'Component not in Inventory'\n",
" df.loc[mask, 'Top Level sharing Components'] = df.loc[mask, 'IDD Component'].apply(lambda x: list_shared_components(x, df))\n",
" \n",
" return df\n",
"\n",
"# Assuming df_CTB_updated is already defined and loaded\n",
"# Call the function to update the DataFrame\n",
"df_CTB_updated = Update_Top_Level_Sharing_Components(df_CTB_updated)\n",
"\n",
"# Save the updated DataFrame to an Excel file\n",
"df_CTB_updated.to_excel(original_input, sheet_name='Clear-to-Build', index=False)\n",
"#################################################################################################################################################\n",
"'''\n",
"#***********************************\n",
"# Sort by BOM Index\n",
"#***********************************\n",
"# Sort df_CTB_updated by 'BOM Index'\n",
"df_CTB_updated.sort_values(by='BOM Index', inplace=True)\n",
"\n",
"# Display the updated DataFrame\n",
"#display(df_CTB_updated)\n",
"\n",
"####################################################################################################################\n",
"########################################## SAVING sheet_CTB ##################################\n",
"###################################################################################################################\n",
"# Load workbook\n",
"workbook = load_workbook(original_input)\n",
"\n",
"# Replace the 'Clear-to-Build' tab with df_CTB_updated\n",
"if 'Clear-to-Build' in workbook.sheetnames:\n",
" worksheet = workbook['Clear-to-Build']\n",
" worksheet.delete_rows(2, worksheet.max_row) # Delete all rows starting from row 2\n",
" \n",
" # Write headers\n",
" for c_idx, header in enumerate(df_CTB_updated.columns, start=1):\n",
" worksheet.cell(row=1, column=c_idx, value=header)\n",
"\n",
" # Write data\n",
" for r_idx, row in enumerate(df_CTB_updated.values, start=2): # Start from row 2\n",
" for c_idx, value in enumerate(row, start=1):\n",
" worksheet.cell(row=r_idx, column=c_idx, value=value)\n",
"else:\n",
" print(\"Error: 'Clear-to-Build' tab not found in the input file.\")\n",
"\n",
"# Save the updated workbook\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"####################################################################################################################\n",
"########################################## Copying Priotity Database ##########################\n",
"###################################################################################################################\n",
"# --> New code 06/28/2024\n",
"# Load workbook and sheet if it exists, otherwise create a new sheet\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" existing_sheet = True\n",
"except FileNotFoundError:\n",
" workbook = None\n",
" existing_sheet = False\n",
"\n",
"if existing_sheet:\n",
" if 'CM-Priority' in workbook.sheetnames:\n",
" sheet_Priority = workbook['CM-Priority']\n",
" else:\n",
" sheet_Priority = workbook.create_sheet(title='CM-Priority', index=4)\n",
"else:\n",
" sheet_Priority = workbook.create_sheet(title='CM-Priority', index=4)\n",
"\n",
"# Clear existing data in the sheet (optional step)\n",
"if existing_sheet and 'CM-Priority' in workbook.sheetnames:\n",
" sheet_Priority.delete_rows(sheet_Priority.min_row + 1, sheet_Priority.max_row)\n",
"\n",
"# Convert 'Earliest Due Date' and 'SO Modified' columns to datetime format\n",
"df_Priority['Earliest Due Date'] = pd.to_datetime(df_Priority['Earliest Due Date'], errors='coerce', format='%m/%d/%Y')\n",
"df_Priority['SO Modified'] = pd.to_datetime(df_Priority['SO Modified'], errors='coerce', format='%m/%d/%Y')\n",
"\n",
"# update 09/09\n",
"# Convert column to string to prevent automatic date parsing\n",
"#df_Priority['Remain. crit. Qty'] = df_Priority['Remain. crit. Qty'].astype(str)\n",
"df_Priority['Remain. crit. Qty'] = df_Priority['Remain. crit. Qty'].fillna(0)\n",
"df_Priority['Remain. crit. Qty'] = df_Priority['Remain. crit. Qty'].astype(int)\n",
"\n",
"#print('df_Priority')\n",
"#display(df_Priority)\n",
"\n",
"# Ensure the column is treated as an object type - Removed 09/09\n",
"#df_Priority['Remain. crit. Qty'] = df_Priority['Remain. crit. Qty'].astype(object)\n",
"\n",
"# Replace 'nan' strings with empty string - Update 09/09\n",
"#df_Priority['Remain. crit. Qty'] = df_Priority['Remain. crit. Qty'].replace('nan', '')\n",
"\n",
"# Convert 'Earliest Due Date' and 'SO Modified' format to short date format (mm/dd/yyyy)\n",
"df_Priority['SO Modified'] = df_Priority['SO Modified'].dt.strftime('%m/%d/%Y')\n",
"df_Priority['Earliest Due Date'] = df_Priority['Earliest Due Date'].dt.strftime('%m/%d/%Y')\n",
"\n",
"# Write headers\n",
"header = list(df_Priority.columns) # Convert Index to list\n",
"sheet_Priority.append(header)\n",
"\n",
"# Write data\n",
"for r in dataframe_to_rows(df_Priority, index=False, header=False):\n",
" sheet_Priority.append(r)\n",
"\n",
"# Save workbook\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Priority database added successfully as |CM-Priority| in {original_input}\")\n",
"\n",
"#End of NEw Code\n",
"\n",
"########################################################################################################################\n",
"############################################### FORMATING output file ####################################\n",
"#######################################################################################################################\n",
"##### load the formatted workbook\n",
"workbook = load_workbook(original_input) \n",
"# Define sheet_CTB\n",
"sheet_CTB = workbook['Clear-to-Build']\n",
"max_row_CTB = sheet_CTB.max_row\n",
"\n",
"###########################################################################################################################################\n",
"###Formatting tab 1 sheet_CTB ### CLEAR TO BUILD\n",
"###########################################################################################################################################\n",
"\n",
"#Last update column S\n",
"content_S_CTB = {\n",
" 1: \"Last update\",\n",
" 2: date.today().strftime(\"%m-%d-%y\"),\n",
" 3: \"Clear to build report: Based on IDD's Inventory & existing BOM\"\n",
"}\n",
"\n",
"# Apply sorting to column I (assuming column I is column 9)\n",
"##sheet_CTB.sort('I2:I' + str(max_row_CTB)) # Sort column I\n",
"\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_CTB[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row of the sheet_CTB worksheet\n",
"sheet_CTB.auto_filter.ref = sheet_CTB.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to R\n",
"for column in sheet_CTB.iter_cols(min_col=1, max_col=18):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['O', 'P', 'Q','R']:\n",
" sheet_CTB.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['M', 'N','J','S']:\n",
" sheet_CTB.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['C', 'F']:\n",
" sheet_CTB.column_dimensions[column_letter].width = 30\n",
" else:\n",
" sheet_CTB.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"\n",
"# Set column widths and text alignment for columns S\n",
"for column in sheet_CTB.iter_cols(min_col=19, max_col=19):\n",
" for cell in column:\n",
" cell.alignment = Alignment(horizontal='left' if cell.row == 1 else 'center', vertical='center')\n",
" \n",
" if column[0].column_letter == 'S':\n",
" sheet_CTB.column_dimensions['S'].width = 15 \n",
"\n",
"#####################################################\n",
"### Set formatting for new columns [T] to [X] ###\n",
"####################################################\n",
"for column in sheet_CTB.iter_cols(min_col=20, max_col=24):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['W']:\n",
" sheet_CTB.column_dimensions[column_letter].width = 60\n",
" sheet_CTB.column_dimensions[column_letter].alignment = Alignment(horizontal='left', vertical='center', wrap_text=True) # Set alignment for column [W] and [X] to left\n",
" elif column_letter in ['T','X']:\n",
" sheet_CTB.column_dimensions[column_letter].width = 35\n",
" else:\n",
" sheet_CTB.column_dimensions[column_letter].width = 20\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"######################################\n",
"######## Color formating ###########\n",
"### Formating [T] 'Max Qty (GS)' ##############################\n",
"# Custom conditional formatting for column T (Max Qty (GS)) ##\n",
"###############################################################\n",
"for row in range(2, max_row_CTB + 1):\n",
" cell_T = sheet_CTB.cell(row=row, column=20) # Assuming 'Max Qty (GS)' is in column T\n",
" cell_O = sheet_CTB.cell(row=row, column=15) # Assuming column O contains the comparison value\n",
" cell_Q = sheet_CTB.cell(row=row, column=17) # Assuming column Q contains the comparison value\n",
" \n",
" if cell_O.value not in ['Completed', 'Canceled', 'TBD', 'To be Canceled']:\n",
" if isinstance(cell_T.value, (int, float)) and isinstance(cell_O.value, (int, float)):\n",
" if cell_T.value == 'N/A' or cell_T.value == 'Floor stock item':\n",
" continue # Skip N/A and Floor stock items\n",
"\n",
" # Convert cell_T.value to numerical type for comparison\n",
" if isinstance(cell_T.value, str):\n",
" cell_T_value = 0 # Or any default value that makes sense in your context\n",
" else:\n",
" cell_T_value = cell_T.value\n",
"\n",
" # Convert cell_Q.value to numerical type for comparison\n",
" if cell_Q.value is None:\n",
" cell_Q_value = 0 # Or any default value that makes sense in your context\n",
" elif isinstance(cell_Q.value, str):\n",
" cell_Q_value = 0 # Or any default value that makes sense in your context\n",
" else:\n",
" cell_Q_value = cell_Q.value\n",
"\n",
" if cell_T_value > cell_Q_value:\n",
" fill_color = 'C6EFCE' # Green\n",
" elif cell_T_value < cell_Q_value:\n",
" # Check if [T] is the lowest number for a given Top-Level [M]\n",
" top_level = sheet_CTB.cell(row=row, column=13).value # Assuming 'IDD Top Level' is in column M\n",
" # Extract numerical values for comparison\n",
" top_level_values = [sheet_CTB.cell(row=r, column=20).value for r in range(2, max_row_CTB + 1) if sheet_CTB.cell(row=r, column=13).value == top_level and isinstance(sheet_CTB.cell(row=r, column=20).value, (int, float))]\n",
" if top_level_values:\n",
" min_value_T = min(top_level_values)\n",
" if cell_T.value == min_value_T:\n",
" fill_color = 'FFC7CE' # Red\n",
" else:\n",
" fill_color = 'FFEB9C' # Yellow\n",
" else:\n",
" fill_color = 'FFEB9C' # Yellow\n",
" else:\n",
" fill_color = None\n",
"\n",
" if fill_color:\n",
" cell_T.fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
"\n",
"#############################################################\n",
"# Set column width and text alignment for column S from row 3\n",
"############################################################\n",
"for cell in sheet_CTB.iter_rows(min_row=3, min_col=19, max_col=19):\n",
" cell[0].alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Apply borders for rows 1 and 2 in column S\n",
"for row in range(1, 3):\n",
" sheet_CTB.cell(row=row, column=19).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell S2 and S3 left align\n",
"sheet_CTB.cell(row=2, column=19).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"# Define the grey fill color for rows related to Component not in Inventory\n",
"grey_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')\n",
"\n",
"# Define the red fill color for column 19 (T)\n",
"red_fill_color = 'FFC7CE' \n",
"blue_fill_color = '92CDDC' \n",
"\n",
"# Iterate through each row in the sheet_CTB worksheet\n",
"for row in sheet_CTB.iter_rows(min_row=2, max_row=max_row_CTB, min_col=1, max_col=20): # Adjust max_col to 20\n",
" inventory_status_cell = row[2] # Assuming 'Inventory Status' is in column C (index 2)\n",
" floor_stock_cell = row[19] # Assuming column T is the 20th column (index 19)\n",
" \n",
" if inventory_status_cell.value == 'Component not in Inventory':\n",
" # Apply grey fill color to columns 1 to 18\n",
" for cell in row[:18]: \n",
" cell.fill = grey_fill\n",
" # Apply red fill color to column 20 (T)\n",
" if floor_stock_cell.value is not None and floor_stock_cell.value not in ['Floor stock item', 'N/A']: # Check if it's not a floor stock item or N/A\n",
" row[19].fill = PatternFill(start_color=red_fill_color, end_color=red_fill_color, fill_type='solid')\n",
" # Apply blue fill color to column 20 (T) if 'Level' is in the value\n",
" if isinstance(floor_stock_cell.value, str) and 'Level' in floor_stock_cell.value: # Check if 'Level' is present in the value\n",
" row[19].fill = PatternFill(start_color=blue_fill_color, end_color=blue_fill_color, fill_type='solid')\n",
" \n",
"# Custom conditional formatting for column K 'Level' (Assuming data starts from row 2)\n",
"min_row = 2\n",
"col_K = 11\n",
"\n",
"# Initialize fill_color outside of the loop\n",
"fill_color = None\n",
"\n",
"for row in range(min_row, max_row_CTB + 1):\n",
" cell_value = sheet_CTB.cell(row=row, column=col_K).value\n",
" \n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" else:\n",
" fill_color = None # Reset fill_color when cell value is None\n",
" \n",
" # Check if fill_color is not None before applying PatternFill\n",
" if fill_color is not None:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_CTB.cell(row=row, column=col_K).fill = fill\n",
"\n",
"# Write the date to a specific cell\n",
"sheet_CTB.cell(row=2, column=19, value=file_date_inventory) # S2 cell\n",
"\n",
"######################################################################################################################################################\n",
"#### Tab 2 - CM-Inventory ### CM-INVENTORY\n",
"###################################################################################################################################################\n",
"sheet_Inventory = workbook['CM-Inventory']\n",
"\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_Inventory[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_Inventory.auto_filter.ref = sheet_Inventory.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to R\n",
"for column in sheet_Inventory.iter_cols(min_col=1, max_col=18):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['G','J','M','N']:\n",
" sheet_Inventory.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['O','P','Q']:\n",
" sheet_Inventory.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['D']:\n",
" sheet_Inventory.column_dimensions[column_letter].width = 30\n",
" else:\n",
" sheet_Inventory.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set column width and text alignment for column S from row 3\n",
"for cell in sheet_Inventory.iter_rows(min_row=3, min_col=19, max_col=19):\n",
" cell[0].alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Apply borders for rows 1 and 2 in column S\n",
"for row in range(1, 3):\n",
" sheet_Inventory.cell(row=row, column=19).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell S2 and S3 left align\n",
"sheet_Inventory.cell(row=2, column=19).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"# Custom conditional formatting for column K (Assuming data starts from row 2)Remain. crit. Qty\n",
"min_row = 2\n",
"max_row = sheet_Inventory.max_row\n",
"col_K = 11\n",
"\n",
"for row in range(min_row, max_row + 1):\n",
" cell_value = sheet_Inventory.cell(row=row, column=col_K).value\n",
" \n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" \n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_Inventory.cell(row=row, column=col_K).fill = fill\n",
"\n",
"################################################################################################################################################ \n",
"#### Tab 3 - CM-BOM ### CM-BOM\n",
"#################################################################################################################################################\n",
"sheet_BOM = workbook['CM-BOM']\n",
"\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_BOM[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_BOM.auto_filter.ref = sheet_BOM.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to L\n",
"for column in sheet_BOM.iter_cols(min_col=1, max_col=11):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['C', 'G', 'H']:\n",
" sheet_BOM.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['F']:\n",
" sheet_BOM.column_dimensions[column_letter].width = 30\n",
" else:\n",
" sheet_BOM.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set column width and text alignment for column L from row 3\n",
"for cell in sheet_BOM.iter_rows(min_row=3, min_col=12, max_col=12):\n",
" cell[0].alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Apply borders for rows 1 and 2 in column L\n",
"for row in range(1, 3):\n",
" sheet_BOM.cell(row=row, column=12).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell L2 and L3 left align\n",
"sheet_BOM.cell(row=2, column=12).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"# Custom conditional formatting for column D (Assuming data starts from row 2)\n",
"min_row = 2\n",
"max_row = sheet_BOM.max_row\n",
"col_D = 4\n",
"\n",
"for row in range(min_row, max_row + 1):\n",
" cell_value = sheet_BOM.cell(row=row, column=col_D).value\n",
" \n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" \n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_BOM.cell(row=row, column=col_D).fill = fill\n",
"\n",
"#####################################################################\n",
"### Save the changes to the Excel file \n",
"#####################################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ####### ######## ######## ## ## ## ######## ########\n",
"## ## ## ## ## ## ## ## ## ## ## ##\n",
"## ###### ######## ## #### ## ## ## ## #####\n",
"## ## ## ## ## ## ## ## ## ## ## ## ##\n",
"## ####### ## ## ######## ## ### ####### ######## #########\n",
"##############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"# Define date and path\n",
"input_backlog_formatted = os.path.join(Path, f'CM_IDD_Backlog-{file_date_inventory}_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |CM-Backlog| ...')\n",
" \n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_backlog = pd.read_excel(input_backlog_formatted, sheet_name=0)\n",
" print(\"Backlog files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input backlog file not found: {e}\")\n",
" exit()\n",
"\n",
"# Convert 'SO Modified' column to datetime format\n",
"df_backlog['SO Modified'] = pd.to_datetime(df_backlog['SO Modified'], errors='coerce', format='%m-%d-%Y')\n",
"\n",
"# Convert datetime format to short date format (mm/dd/yyyy)\n",
"df_backlog['SO Modified'] = df_backlog['SO Modified'].dt.strftime('%m/%d/%Y')\n",
"\n",
"#New 08/20\n",
"#######################################################################################\n",
"# -->> CREATE 'Month', 'Product Category', and 'Complexity' in |CM-Backlog| + 'Month Requested'\n",
"######################################################################################\n",
"# Creating column 'Month'\n",
"############################\n",
"# Ensure 'Due date' is in datetime format\n",
"df_backlog['Due Date'] = pd.to_datetime(df_backlog['Due Date'])\n",
"\n",
"# Directly create 'Month' in the desired format (e.g., Jan 23, Feb 23, ...)\n",
"df_backlog['Month'] = df_backlog['Due Date'].dt.strftime('%b %y')\n",
"\n",
"# Ensure 'Requested Date' is in datetime format\n",
"df_backlog['Requested Date'] = pd.to_datetime(df_backlog['Requested Date'])\n",
"\n",
"# Directly create 'Month Requested' in the desired format (e.g., Jan 23, Feb 23, ...)\n",
"df_backlog['Month Requested'] = df_backlog['Requested Date'].dt.strftime('%b %y')\n",
"\n",
"# Convert 'Due date' back to short date format (e.g., MM/DD/YYYY)\n",
"df_backlog['Due Date'] = df_backlog['Due Date'].dt.strftime('%m/%d/%Y')\n",
"\n",
"# Convert 'Requested Date' back to short date format (e.g., MM/DD/YYYY)\n",
"df_backlog['Requested Date'] = df_backlog['Requested Date'].dt.strftime('%m/%d/%Y')\n",
"\n",
"##########################################################################################################################################\n",
"# Creating column 'Complexity' with the function to define the familly based on 'General Description' + new function to assign a complecity level\n",
"###########################################################################################################################################\n",
"# Define the 'Product Category' base on 'General Description' \n",
"#######################################################\n",
"# Define the 'Product Category' based on 'General Description'\n",
"def determine_category(description):\n",
" if not isinstance(description, str):\n",
" return 'Others'\n",
" if description == 'Rototellite':\n",
" return 'Rototellite'\n",
" elif 'Indicator' in description or 'CPA' in description:\n",
" return 'CPA'\n",
" elif 'Lightplate' in description:\n",
" return 'Lightplate'\n",
" elif 'ISP' in description or 'Keyboard' in description:\n",
" return 'ISP'\n",
" elif 'Module' in description:\n",
" return 'CPA'\n",
" elif 'optics' in description:\n",
" return 'Fiber Optics'\n",
" else:\n",
" return 'Others'\n",
"\n",
"# Create 'Product_Category' column based on the 'General Description'\n",
"df_backlog['Product_Category'] = df_backlog['General Description'].apply(determine_category)\n",
"\n",
"###################################################\n",
"# Assigne a complexity Level base on the familly \n",
"##################################################\n",
"# 'Kit' = 0, 'Rototellite' and 'Lightplete' = 1, 'CPA' = 2, 'ISP' = 3\n",
"# Assign a complexity level based on the category\n",
"def determine_complexity(category):\n",
" if not isinstance(category, str):\n",
" return 0\n",
" if category == 'Rototellite':\n",
" return 0.50\n",
" elif category == 'Lightplate':\n",
" return 0.25\n",
" elif category == 'CPA':\n",
" return 0.75\n",
" elif category == 'ISP':\n",
" return 1\n",
" elif category == 'Fiber Optics':\n",
" return 0.50\n",
" elif category == 'Others':\n",
" return 0\n",
" else:\n",
" return 0\n",
" \n",
"# Create 'Complexity' column based on 'Product Category'\n",
"df_backlog['Complexity'] = df_backlog['Product_Category'].apply(determine_complexity)\n",
"\n",
"#print('df_Historic')\n",
"#display(df_Historic)\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating Backlog #########################\n",
"###################################################################################################################\n",
"# Check if \"CM-Backlog\" sheet already exists and delete it if it does\n",
"if \"CM-Backlog\" in workbook.sheetnames:\n",
" del workbook[\"CM-Backlog\"]\n",
"\n",
"# Create new \"CM-Backlog\" sheet\n",
"backlog_sheet = workbook.create_sheet(title='CM-Backlog')\n",
"\n",
"# Write headers\n",
"for c_idx, header in enumerate(df_backlog.columns, start=1):\n",
" backlog_sheet.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data\n",
"for r_idx, row in enumerate(df_backlog.values, start=2): # Start from row 2\n",
" for c_idx, value in enumerate(row, start=1):\n",
" backlog_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"####################################################################################################################\n",
"########################################## Formating Backlog #########################\n",
"###################################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in backlog_sheet[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"backlog_sheet.auto_filter.ref = backlog_sheet.dimensions\n",
"\n",
"############################################################\n",
"# Set column widths and text alignment for columns A to V\n",
"#############################################################\n",
"for column in backlog_sheet.iter_cols(min_col=1, max_col=22):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['C', 'D','H', 'F', 'G', 'T', 'U']:\n",
" backlog_sheet.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['I', 'N', 'P']:\n",
" backlog_sheet.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['N', 'O', 'U', 'V', 'Q']:\n",
" backlog_sheet.column_dimensions[column_letter].width = 20\n",
" else:\n",
" backlog_sheet.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Erase contents in column W (assuming W is column 23)\n",
"for row in range(2, backlog_sheet.max_row + 1):\n",
" backlog_sheet.cell(row=row, column=23).value = None\n",
"\n",
"# Apply borders for rows 1 and 2 in column W\n",
"for row in range(1, 3):\n",
" backlog_sheet.cell(row=row, column=23).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell W2 and W3 left align\n",
"backlog_sheet.cell(row=2, column=23).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"backlog_sheet.cell(row=2, column=23).alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Set the date of column W2 to {date}\n",
"backlog_sheet.cell(row=2, column=23, value=file_date_inventory)\n",
"\n",
"# Set the width of column W (23th column)\n",
"backlog_sheet.column_dimensions[get_column_letter(23)].width = 20\n",
"\n",
"############################################################################\n",
"# Set column widths and text alignment for columns after W --> X to AA\n",
"############################################################################\n",
"for column in backlog_sheet.iter_cols(min_col=24, max_col=28):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" \n",
" cell.border = Border(\n",
" top=Side(style='thin'),\n",
" right=Side(style='thin'),\n",
" bottom=Side(style='thin'),\n",
" left=Side(style='thin')\n",
" )\n",
"\n",
"# Set the width of column X (24th column)\n",
"backlog_sheet.column_dimensions[get_column_letter(24)].width = 20\n",
"# Set the width of column AA (27th column)\n",
"backlog_sheet.column_dimensions[get_column_letter(27)].width = 20\n",
" \n",
"#**#############################################################################################################**\n",
"# Color formating based on |Production status| from CM-Priority --> Refering to |CM-Priority| formatting \n",
"#**############################################################################################################**\n",
"# Define the fills and fonts for conditional formatting\n",
"green_fill = PatternFill(start_color='D8E4BC', end_color='D8E4BC', fill_type='solid')\n",
"blue_fill = PatternFill(start_color='DAEEF3', end_color='DAEEF3', fill_type='solid')\n",
"dark_red_font = Font(color='C00000')\n",
"light_yellow_fill = PatternFill(start_color='FFFFCC', end_color='FFFFCC', fill_type='solid')\n",
"red_fill = PatternFill(start_color='F2DCDB', end_color='F2DCDB', fill_type='solid')\n",
"grey_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')\n",
"grey_font_color = Font(color='BFBFBF')\n",
"font_color = Font(color='000000') # Black font color\n",
"\n",
"border_color = '000000' # Black color for the border\n",
"thin_black_side = Side(style='thin', color=border_color)\n",
"border_grey = Border(\n",
" left=Side(border_style=None),\n",
" right=Side(border_style=None),\n",
" top=Side(border_style=None),\n",
" bottom=Side(border_style=None),\n",
" diagonal=Side(border_style='thin', color='D9D9D9'),\n",
" diagonalDown=True,\n",
" diagonalUp=True\n",
")\n",
"\n",
"# Load the workbook and sheets\n",
"sheet_Priority = workbook[\"CM-Priority\"]\n",
"\n",
"# Get the column indices (1-based)\n",
"col_idd_top_level_priority = [cell.value for cell in sheet_Priority[1]].index(\"IDD Top Level\") + 1\n",
"col_idd_top_level_backlog = [cell.value for cell in backlog_sheet[1]].index(\"IDD Top Level\") + 1\n",
"col_production_status = [cell.value for cell in sheet_Priority[1]].index(\"Production Status\") + 1\n",
"\n",
"# Create a mapping of IDD Top Level values to their respective formatting in the Priority sheet\n",
"formatting_map = {}\n",
"redlist_map = {}\n",
"completed_map = {}\n",
"\n",
"for row in sheet_Priority.iter_rows(min_row=2, max_row=sheet_Priority.max_row):\n",
" idd_value = row[col_idd_top_level_priority - 1].value # Adjust for zero-based index\n",
" production_status = row[col_production_status - 1].value\n",
"\n",
" if production_status == 'Industrialized':\n",
" formatting_map[idd_value] = green_fill\n",
" elif 'WIP' in str(production_status):\n",
" formatting_map[idd_value] = blue_fill\n",
"\n",
" # Redlist criteria\n",
" value_in_O = row[14].value # Column O is the 15th column\n",
" if value_in_O and ('transferred' in str(value_in_O).lower() or 'canceled' in str(value_in_O).lower()):\n",
" redlist_map[idd_value] = red_fill\n",
"\n",
" # Completed PN\n",
" value_in_I = row[8].value # Column I is the 9th column\n",
" if value_in_I == 'Completed':\n",
" completed_map[idd_value] = (grey_fill, grey_font_color)\n",
"\n",
"# Apply the formatting to the Backlog sheet based on the mappings\n",
"for row in backlog_sheet.iter_rows(min_row=2, max_row=backlog_sheet.max_row):\n",
" idd_value = row[col_idd_top_level_backlog - 1].value # Adjust for zero-based index\n",
" if idd_value in formatting_map:\n",
" fill = formatting_map[idd_value]\n",
" for cell in row[:backlog_sheet.max_column - 6]: # Iterate up to max_column - 6\n",
" cell.fill = fill\n",
" cell.font = font_color\n",
"\n",
" if idd_value in redlist_map:\n",
" fill = redlist_map[idd_value]\n",
" for cell in row[:backlog_sheet.max_column - 6]: # Iterate up to max_column - 6\n",
" cell.fill = fill\n",
" if cell.col_idx == 1 or cell.col_idx == backlog_sheet.max_column:\n",
" cell.border = Border(left=thin_black_side if cell.col_idx == 1 else None,\n",
" right=thin_black_side if cell.col_idx == backlog_sheet.max_column else None,\n",
" top=thin_black_side, bottom=thin_black_side,\n",
" diagonal=border_grey.diagonal, diagonalDown=border_grey.diagonalDown,\n",
" diagonalUp=border_grey.diagonalUp)\n",
"\n",
" if idd_value in completed_map:\n",
" fill, font = completed_map[idd_value]\n",
" for cell in row[:backlog_sheet.max_column - 6]: # Iterate up to max_column - 6\n",
" cell.fill = fill\n",
" cell.font = font\n",
"\n",
"#################################################\n",
"# Formatting SO Modified and Due date\n",
"###############################################\n",
"# Define column indices\n",
"due_date_column_index = 17 # Column Q\n",
"so_modified_column_index = 20 # Column T\n",
"\n",
"# Define the orange fill color\n",
"orange_fill = PatternFill(start_color='FCD5B4', end_color='FCD5B4', fill_type='solid')\n",
"\n",
"# Define the yellow fill color and dark red font for the target date\n",
"light_yellow_fill = PatternFill(start_color='FFFFCC', end_color='FFFFCC', fill_type='solid')\n",
"dark_red_font = Font(color='C00000')\n",
"\n",
"# Loop through the rows in the backlog_sheet\n",
"for row in range(2, backlog_sheet.max_row + 1):\n",
" due_date_cell = backlog_sheet.cell(row=row, column=due_date_column_index)\n",
" so_modified_cell = backlog_sheet.cell(row=row, column=so_modified_column_index)\n",
"\n",
" due_date_value = due_date_cell.value\n",
" so_modified_value = so_modified_cell.value\n",
"\n",
" # If SO Modified is NaN (None), just continue without applying any fill\n",
" if so_modified_value is None or pd.isna(so_modified_value):\n",
" continue\n",
"\n",
" # Apply orange fill if dates are different\n",
" if due_date_value != so_modified_value:\n",
" due_date_cell.fill = orange_fill\n",
" so_modified_cell.fill = orange_fill\n",
" # Optionally, apply dark red font\n",
" due_date_cell.font = dark_red_font\n",
" so_modified_cell.font = dark_red_font\n",
" \n",
"# Additional formatting based on SO Modified/Due date date in column S 'Due Date' \n",
"target_dates = {datetime(2026, 12, 31), datetime(2026, 12, 24)} # 12/31/2026 or 12/24/2026\n",
"for row in range(2, backlog_sheet.max_row + 1):\n",
" value_in_S = backlog_sheet.cell(row=row, column=so_modified_column_index).value # Column S is the 19th column for |CM-Backlog|\n",
" if isinstance(value_in_S, datetime) and value_in_S.date() == target_dates.date():\n",
" cell = backlog_sheet.cell(row=row, column=so_modified_column_index)\n",
" cell.fill = light_yellow_fill\n",
" cell.font = dark_red_font\n",
"\n",
"############################################################################\n",
"# Highlight NC orders in yellow for entire row when 'Order' contains 'NC'\n",
"#############################################################################\n",
"yellow_fill = PatternFill(start_color='FFFF99', end_color='FFFF99', fill_type='solid')\n",
"\n",
"for row in backlog_sheet.iter_rows(min_row=2, max_row=backlog_sheet.max_row):\n",
" order_cell = row[11] # Column L (12th column, zero-indexed is 11)\n",
" if order_cell.value and 'NC' in order_cell.value:\n",
" for cell in row[:backlog_sheet.max_column - 5]: # Iterate up to max_column - 1\n",
" cell.fill = yellow_fill\n",
"\n",
"#####################################################################################################\n",
"# Fill cell color based on condition for 'Order' column\n",
"#####################################################################################################\n",
"\n",
"# Define the fill color for 'Order' column\n",
"light_purple_fill = PatternFill(start_color='E4DFEC', end_color='E4DFEC', fill_type='solid')\n",
"\n",
"# Iterate through rows and fill 'Order' column based on condition\n",
"for row in range(2, backlog_sheet.max_row + 1):\n",
" order_cell = backlog_sheet.cell(row=row, column=12) # Column L (1-based index)\n",
" if order_cell.value and str(order_cell.value).startswith('D'):\n",
" order_cell.fill = light_purple_fill\n",
" \n",
"####################################################################\n",
"# Save the updated workbook\n",
"###################################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Backlog added successfully as |CM-Backlog| in {original_input}\")\n",
"\n",
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ############## ########### #########\n",
"## ## ## ## ## ##\n",
"## ## ## ## ##########\n",
"## ## ## ## ## ###\n",
"## ## URN ###########VER ## ### EPORT \n",
"##############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"# Define date and path\n",
"input_TurnoverReport_formatted = os.path.join(Path, f'CM_IDD_TurnoverReport-{file_date_inventory}_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |CM-TurnoverReport| ...')\n",
"\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_TurnoverReport = pd.read_excel(input_TurnoverReport_formatted, sheet_name=0)\n",
" #print(\"TurnoverReport files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input TurnoverReport file not found: {e}\")\n",
" exit()\n",
"\n",
"# Convert 'SO Modified' column to datetime format\n",
"df_TurnoverReport['SO Modified'] = pd.to_datetime(df_TurnoverReport['SO Modified'], errors='coerce', format='%m-%d-%Y')\n",
"\n",
"# Convert datetime format to short date format (mm/dd/yyyy)\n",
"df_TurnoverReport['SO Modified'] = df_TurnoverReport['SO Modified'].dt.strftime('%m/%d/%Y')\n",
"\n",
"#Rename column ['Flight/AWB'] and convert to number to avoid the format Number+11\n",
"df_TurnoverReport = df_TurnoverReport.rename(columns={'Flight/AWB': 'Tracking#'})\n",
"\n",
"# Convert 'Tracking#' column to string\n",
"df_TurnoverReport['Tracking#'] = df_TurnoverReport['Tracking#'].astype(str)\n",
"\n",
"# Remove '.0' from the string if it exists\n",
"df_TurnoverReport['Tracking#'] = df_TurnoverReport['Tracking#'].str.replace('.0', '', regex=False)\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating TurnoverReport #########################\n",
"###################################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if \"CM-TurnoverReport\" in workbook.sheetnames:\n",
" del workbook[\"CM-TurnoverReport\"]\n",
"\n",
"# Create new \"CM-TurnoverReport\" sheet\n",
"TurnoverReport_sheet = workbook.create_sheet(title='CM-TurnoverReport')\n",
"\n",
"# Write headers\n",
"for c_idx, header in enumerate(df_TurnoverReport.columns, start=1):\n",
" TurnoverReport_sheet.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data\n",
"for r_idx, row in enumerate(df_TurnoverReport.values, start=2): # Start from row 2\n",
" for c_idx, value in enumerate(row, start=1):\n",
" TurnoverReport_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"####################################################################################################################\n",
"########################################## Formating TurnoverReport #########################\n",
"###################################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in TurnoverReport_sheet[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"TurnoverReport_sheet.auto_filter.ref = TurnoverReport_sheet.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to W\n",
"for column in TurnoverReport_sheet.iter_cols(min_col=1, max_col=22):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['A', 'B', 'F', 'G', 'M', 'P', 'I', 'Q', 'S', 'R']:\n",
" TurnoverReport_sheet.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['H', 'L', 'N']:\n",
" TurnoverReport_sheet.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['C', 'D', 'N', 'T', 'U', 'V', 'R']:\n",
" TurnoverReport_sheet.column_dimensions[column_letter].width = 20\n",
" else:\n",
" backlog_sheet.column_dimensions[column_letter].width = 10\n",
"\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Apply borders for rows 1 and 2 in column W\n",
"for row in range(1, 3):\n",
" TurnoverReport_sheet.cell(row=row, column=23).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell W2 and W3 left align\n",
"TurnoverReport_sheet.cell(row=2, column=23).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"TurnoverReport_sheet.cell(row=2, column=23).alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"#**#############################################################################################################**\n",
"# Color formating based on |Production status| from CM-Priority --> Refering to |CM-Priority| formatting \n",
"#**############################################################################################################**\n",
"# Define the fills and fonts for conditional formatting\n",
"green_fill = PatternFill(start_color='D8E4BC', end_color='D8E4BC', fill_type='solid')\n",
"blue_fill = PatternFill(start_color='DAEEF3', end_color='DAEEF3', fill_type='solid')\n",
"dark_red_font = Font(color='C00000')\n",
"light_yellow_fill = PatternFill(start_color='FFFFCC', end_color='FFFFCC', fill_type='solid')\n",
"red_fill = PatternFill(start_color='F2DCDB', end_color='F2DCDB', fill_type='solid')\n",
"grey_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')\n",
"grey_font_color = Font(color='BFBFBF')\n",
"font_color = Font(color='000000') # Black font color\n",
"\n",
"border_color = '000000' # Black color for the border\n",
"thin_black_side = Side(style='thin', color=border_color)\n",
"border_grey = Border(\n",
" left=Side(border_style=None),\n",
" right=Side(border_style=None),\n",
" top=Side(border_style=None),\n",
" bottom=Side(border_style=None),\n",
" diagonal=Side(border_style='thin', color='D9D9D9'),\n",
" diagonalDown=True,\n",
" diagonalUp=True\n",
")\n",
"\n",
"# Load the workbook and sheets\n",
"sheet_Priority = workbook[\"CM-Priority\"]\n",
"\n",
"# Get the column indices (1-based)\n",
"col_idd_top_level_priority = [cell.value for cell in sheet_Priority[1]].index(\"IDD Top Level\") + 1\n",
"col_idd_top_level_TurnoverReport = [cell.value for cell in TurnoverReport_sheet[1]].index(\"IDD Top Level\") + 1\n",
"col_production_status = [cell.value for cell in sheet_Priority[1]].index(\"Production Status\") + 1\n",
"\n",
"# Create a mapping of IDD Top Level values to their respective formatting in the Priority sheet\n",
"formatting_map = {}\n",
"redlist_map = {}\n",
"completed_map = {}\n",
"\n",
"for row in sheet_Priority.iter_rows(min_row=2, max_row=sheet_Priority.max_row):\n",
" idd_value = row[col_idd_top_level_priority - 1].value # Adjust for zero-based index\n",
" production_status = row[col_production_status - 1].value # Adjust for zero-based index\n",
"\n",
" if production_status == 'Industrialized':\n",
" formatting_map[idd_value] = green_fill\n",
" elif 'WIP' in str(production_status):\n",
" formatting_map[idd_value] = blue_fill\n",
"\n",
" # Redlist criteria\n",
" value_in_O = row[14].value # Column O is the 15th column\n",
" if value_in_O and ('transferred' in str(value_in_O).lower() or 'canceled' in str(value_in_O).lower()):\n",
" redlist_map[idd_value] = red_fill\n",
"\n",
" # Completed PN\n",
" value_in_I = row[8].value # Column I is the 9th column\n",
" if value_in_I == 'Completed':\n",
" completed_map[idd_value] = (grey_fill, grey_font_color)\n",
"\n",
"# Apply the formatting to the TurnoverReport sheet based on the mappings\n",
"for row in TurnoverReport_sheet.iter_rows(min_row=2, max_row=TurnoverReport_sheet.max_row):\n",
" idd_value = row[col_idd_top_level_TurnoverReport - 1].value # Adjust for zero-based index\n",
" \n",
" # Apply formatting based on formatting_map\n",
" if idd_value in formatting_map:\n",
" fill = formatting_map[idd_value]\n",
" for cell in row[:backlog_sheet.max_column - 1]: # Iterate up to max_column - 1\n",
" cell.fill = fill\n",
" cell.font = font_color\n",
" \n",
" # Apply formatting based on redlist_map\n",
" if idd_value in redlist_map:\n",
" fill = redlist_map[idd_value]\n",
" for cell in row[:backlog_sheet.max_column - 1]: # Iterate up to max_column - 1\n",
" cell.fill = fill\n",
" if cell.col_idx == 1 or cell.col_idx == TurnoverReport_sheet.max_column:\n",
" cell.border = Border(\n",
" left=thin_black_side if cell.col_idx == 1 else None,\n",
" right=thin_black_side if cell.col_idx == TurnoverReport_sheet.max_column else None,\n",
" top=thin_black_side, bottom=thin_black_side,\n",
" diagonal=border_grey.diagonal, diagonalDown=border_grey.diagonalDown,\n",
" diagonalUp=border_grey.diagonalUp\n",
" )\n",
" \n",
" # Apply formatting based on completed_map\n",
" if idd_value in completed_map:\n",
" fill, font = completed_map[idd_value]\n",
" for cell in row[:backlog_sheet.max_column - 1]: # Iterate up to max_column - 1\n",
" cell.fill = fill\n",
" cell.font = font\n",
"\n",
"############################\n",
"# Higlight NC order in yellow for column J 'Order'\n",
"#############################\n",
"# Highlight NC orders in yellow for entire row when 'Order' contains 'NC'\n",
"yellow_fill = PatternFill(start_color='FFFF99', end_color='FFFF99', fill_type='solid')\n",
"\n",
"# Iterate through rows starting from the second row (index 1 in Python)\n",
"for row in range(2, TurnoverReport_sheet.max_row + 1):\n",
" order_value = TurnoverReport_sheet.cell(row=row, column=10).value # Assuming 'Order' is in column J (10th column)\n",
" if order_value and 'NC' in str(order_value): # Check if 'NC' is in the 'Order' cell\n",
" # Iterate through all cells in the current row (columns A to W)\n",
" for col in range(1, 23): # Adjust max_col based on your actual number of columns\n",
" cell = TurnoverReport_sheet.cell(row=row, column=col)\n",
" cell.fill = yellow_fill\n",
"\n",
"####################################################################\n",
"# Save the updated workbook\n",
"###################################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"TurnoverReport added successfully as |CM-TurnoverReport| in {original_input}\")\n",
"\n",
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ## ## #### ##########\n",
"## ## ## ## ## ## ##\n",
"## ## ## ## ## ## ##########\n",
"## ## ## ## ## ## ##\n",
"## ### ## #### ##\n",
"##############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"# Define date and path\n",
"input_WIP_formatted = os.path.join(Path, f'CM_IDD_WIP-{file_date_inventory}_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |CM-WIP| ...')\n",
"\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_WIP = pd.read_excel(input_WIP_formatted, sheet_name=0)\n",
" print(\"WIP files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input WIP file not found: {e}\")\n",
" exit()\n",
"\n",
"######################################################################################################\n",
"########################################## Creating WIP #########################\n",
"######################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if 'CM-WIP' in workbook.sheetnames:\n",
" del workbook['CM-WIP']\n",
"\n",
"# Create new \"CM-TurnoverReport\" sheet\n",
"sheet_WIP = workbook.create_sheet(title='CM-WIP')\n",
"\n",
"#####################################################\n",
"#Formating of the datafram before writing into Excel \n",
"#####################################################\n",
"# Ensure 'Last movement' is in datetime format\n",
"df_WIP['Last movement'] = pd.to_datetime(df_WIP['Last movement'])\n",
"\n",
"# Make a copy of df_WIP for processing\n",
"df_WIP_Temp = df_WIP.copy()\n",
"\n",
"# Get today's date\n",
"today = pd.Timestamp(datetime.today().date())\n",
"\n",
"# Calculate the absolute difference between 'Last movement' and today's date in df_WIP_Temp\n",
"df_WIP_Temp['Date Difference'] = abs(df_WIP_Temp['Last movement'] - today)\n",
"\n",
"# Define a function to select the row with the smallest date difference for each group\n",
"def select_closest_to_today(group):\n",
" return group.sort_values('Date Difference').head(1)\n",
"\n",
"# Apply the function to each group defined by 'WO' and 'Pty Indice'\n",
"#df_WIP_Temp = df_WIP_Temp.groupby(['WO', 'Pty Indice']).apply(select_closest_to_today).reset_index(drop=True) # Update 09/24 to avoid warning\n",
"#df_WIP_Temp = df_WIP_Temp.groupby(['WO', 'Pty Indice']).apply(select_closest_to_today).reset_index(drop=True).copy() # Update 10/28\n",
"df_WIP_Temp = df_WIP_Temp.groupby(['WO', 'Pty Indice'], group_keys=False).apply(select_closest_to_today).reset_index(drop=True)\n",
"\n",
"\n",
"# Add or update 'OP Status' column with 'Most recent Op'\n",
"df_WIP_Temp['OP Status'] = 'Most recent Op'\n",
"\n",
"# Create a mapping of ('WO', 'Pty Indice', 'Op', 'Last movement') to 'OP Status'\n",
"op_status_mapping = df_WIP_Temp.set_index(['WO', 'Pty Indice', 'Op', 'Last movement'])['OP Status']\n",
"\n",
"# Map 'OP Status' based on the created mapping to update df_WIP\n",
"df_WIP['OP Status'] = df_WIP.set_index(['WO', 'Pty Indice', 'Op', 'Last movement']).index.map(op_status_mapping)\n",
"\n",
"# Apply a lambda function to fill 'OP Status' with 'Most recent Op' where applicable\n",
"df_WIP['OP Status'] = df_WIP.apply(\n",
" lambda row: 'Most recent Op' if (row['WO'], row['Pty Indice'], row['Op'], row['Last movement']) in op_status_mapping.index else row['OP Status'],\n",
" axis=1\n",
")\n",
"\n",
"# Convert 'Last movement' back to short format date sorting\n",
"df_WIP['Last movement'] = df_WIP['Last movement'].dt.strftime('%m/%d/%Y')\n",
"\n",
"# Remove duplicate lines\n",
"df_WIP = df_WIP.drop_duplicates()\n",
"\n",
"#print('df_WIP_Temp after processing:')\n",
"#display(df_WIP_Temp)\n",
"\n",
"#print('df_WIP after processing:')\n",
"#display(df_WIP)\n",
"\n",
"#############################################################################\n",
"# Droping duplicates 'WO' for a given 'Pty Indice' to faciliate the reading\n",
"#############################################################################\n",
"# Retain only unique 'WO' values on column [F] - Identify the duplicates in the 'WO' column\n",
"#df_WIP['WO'] = df_WIP['WO'].where(~df_WIP['WO'].duplicated(), np.nan)\n",
"\n",
"# Get all unique 'Pty Indice' values\n",
"unique_p_indices = df_WIP['Pty Indice'].unique()\n",
"\n",
"# Iterate over each unique 'Pty Indice'\n",
"for pty_indice in unique_p_indices:\n",
" # Filter DataFrame for the current 'Pty Indice'\n",
" df_filtered = df_WIP[df_WIP['Pty Indice'] == pty_indice].copy()\n",
" \n",
" # Identify duplicates in 'WO' column within the filtered DataFrame\n",
" df_filtered['WO'] = df_filtered['WO'].where(~df_filtered['WO'].duplicated(), np.nan)\n",
" \n",
" # Update the original DataFrame with the modified 'WO' column\n",
" df_WIP.loc[df_WIP['Pty Indice'] == pty_indice, 'WO'] = df_filtered['WO']\n",
"\n",
"#######################################################\n",
"# Write headers\n",
"for c_idx, header in enumerate(df_WIP.columns, start=1):\n",
" sheet_WIP.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data\n",
"for r_idx, row in enumerate(df_WIP.values, start=2): # Start from row 2\n",
" for c_idx, value in enumerate(row, start=1):\n",
" sheet_WIP.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"####################################################################################################################\n",
"########################################## Formating WIP #########################\n",
"###################################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_WIP[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_WIP.auto_filter.ref = workbook.active.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to Z\n",
"for column in sheet_WIP.iter_cols(min_col=1, max_col=30):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['M', 'N']:\n",
" sheet_WIP.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['C', 'D', 'E', 'P', 'V', 'Z', 'AA', 'AB']:\n",
" sheet_WIP.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['L', 'W', 'H']:\n",
" sheet_WIP.column_dimensions[column_letter].width = 30\n",
" else:\n",
" sheet_WIP.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set column width and text alignment for column AA from row 3\n",
"for cell in sheet_WIP.iter_rows(min_row=3, min_col=31, max_col=31):\n",
" cell[0].alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Apply borders for rows 1 and 2 in column \n",
"for row in range(1, 3):\n",
" sheet_WIP.cell(row=row, column=31).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell AA2 and AA3 left align\n",
"sheet_WIP.cell(row=2, column=31).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"# Custom conditional formatting for column J 'Level' (Assuming data starts from row 2)\n",
"min_row = 2\n",
"max_row = sheet_WIP.max_row\n",
"col_J = 10\n",
"\n",
"for row in range(min_row, max_row + 1):\n",
" cell_value = sheet_WIP.cell(row=row, column=col_J).value\n",
"\n",
" # Define default fill color in case cell_value is not in expected range\n",
" fill_color = None\n",
" \n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" \n",
" # Create PatternFill object only if fill_color is defined\n",
" if fill_color:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_WIP.cell(row=row, column=col_J).fill = fill\n",
"\n",
"#####################\n",
"#Formating row of WOs\n",
"######################\n",
"# Define thick borders\n",
"thick_border = Border(top=Side(style='thick'), bottom=Side(style='thick'))\n",
"\n",
"# Define bold font\n",
"bold_font = Font(bold=True)\n",
"\n",
"# Highlight color\n",
"bleu_fill = PatternFill(start_color='DAEEF3', end_color='DAEEF3', fill_type='solid')\n",
"# Define fill for dark blue\n",
"dark_blue_fill = PatternFill(start_color='92CDDC', end_color='92CDDC', fill_type='solid')\n",
"\n",
"# Iterate through rows starting from the second row\n",
"for row in range(2, sheet_WIP.max_row + 1): # Make sure to include the last row\n",
" wo_value = sheet_WIP.cell(row=row, column=6).value # 'WO' column is F (6th column)\n",
" \n",
" # Check if 'WO' value is not None or NaN\n",
" if wo_value is not None and not pd.isna(wo_value): \n",
" # Highlight entire row except column J 'Level'\n",
" for col in range(1, sheet_WIP.max_column): # change 07/30\n",
" if col != 10: # Skip column J (10th column)\n",
" cell = sheet_WIP.cell(row=row, column=col)\n",
" cell.fill = bleu_fill\n",
" cell.border = thick_border\n",
" \n",
" # Make the 'WO' value bold and dark bleu in column F\n",
" wo_cell = sheet_WIP.cell(row=row, column=6) # 'WO' column is F (6th column)\n",
" wo_cell.font = bold_font\n",
" wo_cell.fill = dark_blue_fill\n",
" \n",
"######################################################################\n",
"# Formating row of 'WO status' and 'Area' to highlight the current WC \n",
"######################################################################\n",
"# Column indices for 'Op Status', 'WO', and 'Last Movement'\n",
"op_status_col_index = 22 # 'Op Status' column is V (22nd column)\n",
"wo_status_col_index = 7 # 'WO Status' column is G (7th column)\n",
"wo_col_index = 6 # 'WO' column is F (6th column)\n",
"area_col_index = 8 # 'Area' column is H (8th column)\n",
"last_movement_col_index = 28 # 'Last Movement' column is AB (28th column)\n",
"\n",
"# Iterate through the rows to find and highlight the rows where 'Op Status' contains 'Most recent Op'\n",
"for row in range(2, sheet_WIP.max_row + 1):\n",
" op_status = sheet_WIP.cell(row=row, column=op_status_col_index).value # 'Op Status' column\n",
"\n",
" # Check if 'Op Status' is a string and contains 'Most recent Op'\n",
" if isinstance(op_status, str) and 'Most recent Op' in op_status:\n",
" # Highlight the 'Last Movement' cell\n",
" cell = sheet_WIP.cell(row=row, column=last_movement_col_index)\n",
" cell.fill = dark_blue_fill\n",
" cell.font = bold_font # Make the text bold\n",
" \n",
" # Highlight the 'WO status' cell\n",
" cell = sheet_WIP.cell(row=row, column=wo_status_col_index)\n",
" cell.fill = dark_blue_fill\n",
" cell.font = bold_font # Make the text bold\n",
"\n",
" # Highlight the 'Area' cell\n",
" cell = sheet_WIP.cell(row=row, column=area_col_index)\n",
" cell.fill = dark_blue_fill\n",
" cell.font = bold_font # Make the text bold\n",
"\n",
"############################\n",
"# Higlight NC order in yellow for column F 'WO'\n",
"#############################\n",
"# Highlight NC orders in yellow for entire row when 'Order' contains 'NC'\n",
"yellow_fill = PatternFill(start_color='FFFF99', end_color='FFFF99', fill_type='solid')\n",
"\n",
"# Iterate through rows starting from the second row (index 1 in Python)\n",
"for row in range(2, max_row + 1):\n",
" order_value = sheet_WIP.cell(row=row, column=6).value # Assuming 'WO' is in column F (6th column)\n",
" if order_value and 'NC' in str(order_value): # Check if 'NC' is in the 'WO' cell\n",
" # Iterate through all cells in the current row (columns A to Z)\n",
" for col in range(1, 31): # Adjust max_col based on your actual number of columns\n",
" if col == 10: # Skip column J (10th column)\n",
" continue\n",
" cell = sheet_WIP.cell(row=row, column=col)\n",
" cell.fill = yellow_fill\n",
" \n",
"####################################################################\n",
"# Save the updated workbook\n",
"###################################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"WIP added successfully as |CM-WIP| in {original_input}\")\n",
"\n",
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ########## #########\n",
"## ## ## ## ##\n",
"## ########## #########\n",
"## ## #####\n",
"## ## ENDING ## #### EPORT\n",
"##############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"# Define date and path\n",
"input_PendingReport_formatted = os.path.join(Path, f'CM_IDD_PendingReport_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |PendingReport| ...')\n",
"\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_PendingReport = pd.read_excel(input_PendingReport_formatted, sheet_name=0)\n",
" #print(\"Pending Report files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input PendingReport file not found: {e}\")\n",
" exit()\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating |PendingReport| #########################\n",
"###################################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if 'PendingReport' in workbook.sheetnames:\n",
" del workbook['PendingReport']\n",
"\n",
"# Create new \"CM-TurnoverReport\" sheet\n",
"sheet_PendingReport = workbook.create_sheet(title='PendingReport')\n",
"\n",
"# Convert 'S.O. Date' and 'Rel Date' to datetime format (if they are not already)\n",
"df_PendingReport['S.O. Date'] = pd.to_datetime(df_PendingReport['S.O. Date'], errors='coerce')\n",
"df_PendingReport['Rel Date'] = pd.to_datetime(df_PendingReport['Rel Date'], errors='coerce')\n",
"\n",
"####################################################################################################################################################################\n",
"# Including additionnal column based on CM-Priority ['Agile expected status'], and based on BOM ['Description Component'] based on ['Item Number'] from pending report\n",
"####################################################################################################################################################################\n",
"### Create column ['Agile expected status'] based on ['IDD Top Level'] and mapping with ['IDD Top Level'] from df_Priority \n",
"# Merge to include 'Agile expected status' while keeping all existing columns\n",
"# Remove duplicates in df_Priority before merging for 'Agile expected status'\n",
"df_Priority_unique = df_Priority[['IDD Top Level', 'Agile expected status']].drop_duplicates()\n",
"\n",
"#Create a temporary datafram df_PendingReport_temp\n",
"df_PendingReport_temp = df_PendingReport.copy()\n",
"\n",
"# Merge to include 'Agile expected status'\n",
"df_PendingReport_temp = df_PendingReport_temp.merge(\n",
" df_Priority_unique,\n",
" on='IDD Top Level',\n",
" how='left'\n",
")\n",
"\n",
"# Check if 'Agile expected status' is present after the merge\n",
"#print('Check if Agile expected status is part of the datafram After merging with df_Priority:')\n",
"#print(df_PendingReport_temp.head())\n",
"\n",
"# Remove duplicates in df_CM_BOM before merging for 'Description Component'\n",
"df_CM_BOM_unique = df_CM_BOM[['IDD Component', 'Description Component']].drop_duplicates()\n",
"\n",
"# Merge to include 'Description Component'\n",
"df_PendingReport_temp = df_PendingReport_temp.merge(\n",
" df_CM_BOM_unique,\n",
" left_on='Item Number',\n",
" right_on='IDD Component',\n",
" how='left'\n",
")\n",
"\n",
"#Drop 'IDD Component' and 'Description Component' to avoid dupliacted column later\n",
"df_PendingReport_temp.drop(columns=['IDD Component', 'Description Component'], inplace=True)\n",
"\n",
"#print('Check if Agile expected status is part of the datafram After merging with df_CM_BOM_unique:')\n",
"#print(df_PendingReport_temp.head())\n",
"\n",
"### Create column ['Description Component'] based on ['Item Number'] from pending report\n",
"## Mapping of ['Item Number'] from df_PendingReport on ['IDD Component'] from df_CM_BOM and retuning ['Description Component']\n",
"# Remove duplicates before next merge\n",
"df_CM_BOM_unique = df_CM_BOM[['IDD Component', 'Description Component']].drop_duplicates()\n",
"\n",
"# Merge to include 'Description Component' while keeping all existing columns\n",
"df_PendingReport_temp = df_PendingReport_temp.merge(\n",
" df_CM_BOM_unique,\n",
" left_on='Item Number',\n",
" right_on='IDD Component',\n",
" how='left'\n",
")\n",
"\n",
"#print('df_PendingReport_temp BEFORE merging on Production status')\n",
"#display(df_PendingReport_temp)\n",
"\n",
"### Create column ['Production Status'] based on mapping with ['IDD Top Level'] from df_Priority and fill the 'Legacy' PN with 'Industrilalized' and the Not Transfer PN with 'Not Transfer'\n",
"# Merge to include 'Production Status' while keeping all existing columns\n",
"# Remove duplicates in df_Priority before merging for 'Production Status'\n",
"df_Priority_unique = df_Priority[['IDD Top Level', 'Production Status']].drop_duplicates(subset=['IDD Top Level'])\n",
"\n",
"#df_Priority_unique = df_Priority.groupby('IDD Top Level').agg({'Production Status': 'first'}).reset_index()\n",
"\n",
"# Merge to include 'Production Status'\n",
"df_PendingReport_temp = df_PendingReport_temp.merge(\n",
" df_Priority_unique,\n",
" on='IDD Top Level',\n",
" how='left'\n",
")\n",
"\n",
"# Function to determine 'Production Status'\n",
"def determine_production_status(row):\n",
" if row['Program'] in ['Legacy', 'Legacy CUU', 'SIKORSKY', 'COMAC', 'EMBRAER']:\n",
" return 'Industrialized'\n",
" elif row['Program'] == 'Not Transfer':\n",
" return 'Not Transfer'\n",
" return row['Production Status'] # Keep the existing value\n",
"\n",
"# Apply function to update 'Production Status'\n",
"df_PendingReport_temp['Production Status'] = df_PendingReport_temp.apply(determine_production_status, axis=1)\n",
"\n",
"\n",
"####################################################################################################################################\n",
"# Function to order the list based on type of order DO/DX ['S.O. #'], ['Production Status'], and Expected Release date ['Rel Date']\n",
"###################################################################################################################################\n",
"## Order is defined as follow: Collumn ['ENG Priority']\n",
"#1. Industrialized PN - 'Legacy' are considered Industrialized, 'Not Tranfer' are also considered Industrialized due to lack of info\n",
"#2. DO/DX order \n",
"#3. Expected released date\n",
"# --> Top priority is ['ENG Priority'] = Industrialized with ['S.O. #'] start with 'D' and ['Rel Date'] most in the past \n",
"\n",
"# Step 1: Create helper columns to classify Industrialized and Phase 4 industrialized and DO/DX orders\n",
"df_PendingReport_temp['Is Industrialized'] = df_PendingReport_temp['Production Status'].isin(['Industrialized', 'Not Transfer'])\n",
"df_PendingReport_temp['Phase 4 Industrialized'] = (df_PendingReport_temp['Program'] == 'Phase 4') & (df_PendingReport_temp['Production Status'].isin(['Industrialized']))\n",
"df_PendingReport_temp['S.O. # Starts with D'] = df_PendingReport_temp['S.O. #'].apply(lambda x: str(x).startswith('D'))\n",
"\n",
"# Step 2: Define the sorting criteria\n",
"df_PendingReport_temp['Sort Key'] = (\n",
" df_PendingReport_temp['Phase 4 Industrialized'].astype(int) * 1000 + # Phase 4 Industrialized first\n",
" df_PendingReport_temp['Is Industrialized'].astype(int) * 100 + # Other Industrialized next\n",
" df_PendingReport_temp['S.O. # Starts with D'].astype(int) * 10 + # S.O. # starts with D within above groups\n",
" (df_PendingReport_temp['Rel Date'].max() - df_PendingReport_temp['Rel Date']).dt.days # Earliest Rel Date\n",
")\n",
"\n",
"# Adjust the sorting criteria to handle 'S.O. # Starts with D' within each group\n",
"df_PendingReport_temp['Sort Key'] = (\n",
" df_PendingReport_temp['Phase 4 Industrialized'].astype(int) * 1000 +\n",
" df_PendingReport_temp['Is Industrialized'].astype(int) * 100 +\n",
" df_PendingReport_temp['S.O. # Starts with D'].astype(int) * 10 +\n",
" df_PendingReport_temp['Rel Date'].rank(ascending=True, method='first') # Rank Rel Date within each group\n",
")\n",
"\n",
"# Step 3: Sort the DataFrame based on the Sort Key\n",
"df_PendingReport_temp = df_PendingReport_temp.sort_values(by=['Sort Key'], ascending=True)\n",
"\n",
"# Step 4: Assign sequential ENG Priority based on the sorted DataFrame\n",
"df_PendingReport_temp['ENG Priority'] = range(1, len(df_PendingReport_temp ) + 1)\n",
"\n",
"#print('df_PendingReport_temp before droping the extra column used for sorting and after seting priority numbers in [ENG Priority] based on sorting criteria')\n",
"#display(df_PendingReport_temp)\n",
"\n",
"# step 5: Sort dataframe back in the original order based on the index.\n",
"df_PendingReport_temp = df_PendingReport_temp.sort_index()\n",
"\n",
"#print('df_PendingReport_temp after sorting back to original order of df_PendingReport but with [ENG Priority] filled')\n",
"#display(df_PendingReport_temp)\n",
"\n",
"\n",
"####################################################################################################################\n",
"# Creating |PendingReport|\n",
"#####################################################################################################################\n",
"#Create df_PendingReport with relevant column after sortung back to original order basec on the original index\n",
"df_PendingReport = df_PendingReport_temp.drop(columns=['Is Industrialized', 'S.O. # Starts with D', 'Sort Key', 'Phase 4 Industrialized'])\n",
"\n",
"# Format dates as short dates\n",
"df_PendingReport['S.O. Date'] = df_PendingReport['S.O. Date'].dt.strftime('%m/%d/%Y') # MM/DD/YYYY format\n",
"df_PendingReport['Rel Date'] = df_PendingReport['Rel Date'].dt.strftime('%m/%d/%Y') # MM/DD/YYYY format\n",
"\n",
"# Write headers to Excel\n",
"for c_idx, header in enumerate(df_PendingReport.columns, start=1):\n",
" sheet_PendingReport.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data to Excel\n",
"for r_idx, row in enumerate(df_PendingReport.values, start=2):\n",
" for c_idx, value in enumerate(row, start=1):\n",
" sheet_PendingReport.cell(row=r_idx, column=c_idx, value=value)\n",
" \n",
"####################################################################################################################\n",
"########################################## Formating |PendingReport| #########################\n",
"###################################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_PendingReport[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_PendingReport.auto_filter.ref = sheet_PendingReport.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to S\n",
"for column in sheet_PendingReport.iter_cols(min_col=1, max_col=19):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['A', 'B', 'F']:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['G', 'Q', 'O']:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 30\n",
" elif column_letter in ['P']:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['C', 'D', 'H' , 'I', 'N', 'R', 'S']:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 20\n",
" else:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Erase contents in column T (assuming T is column 20)\n",
"for row in range(2, sheet_PendingReport.max_row + 1):\n",
" sheet_PendingReport.cell(row=row, column=10).value = None\n",
"\n",
"# Apply borders for rows 1 and 2 in column T\n",
"for row in range(1, 3):\n",
" sheet_PendingReport.cell(row=row, column=20).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell T2 and T3 left align\n",
"sheet_PendingReport.cell(row=2, column=20).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"sheet_PendingReport.cell(row=2, column=20).alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"#############################################################################################################################\n",
"# Formatting added column ['Agile expected status'], ['Description Component'], ['IDD Component'] and ['Production Status']\n",
"############################################################################################################################\n",
"# Set the width for column T (20)\n",
"sheet_PendingReport.column_dimensions['T'].width = 20\n",
" \n",
"# Set column widths and text alignment for columns U to Y (21 to 25)\n",
"for column in sheet_PendingReport.iter_cols(min_col=21, max_col=25):\n",
" column_letter = column[0].column_letter # Get the column letter\n",
"\n",
" # Set the width based on the column letter\n",
" if column_letter in ['U', 'V', 'X']:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 20\n",
" elif column_letter == 'W':\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 30\n",
" else:\n",
" sheet_PendingReport.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to each cell in the current column\n",
" for cell in column:\n",
" if cell.row == 1: # Header\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else: # Data rows\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
"\n",
" # Set borders for the cells in the current column\n",
" cell.border = Border(\n",
" top=Side(style='thin'),\n",
" right=Side(style='thin'),\n",
" bottom=Side(style='thin'),\n",
" left=Side(style='thin')\n",
" )\n",
" \n",
"########################################\n",
"# Formatting based on 'Rel Date'\n",
"#########################################\n",
"# Convert 'S.O. Date' and 'Rel Date' to datetime format (if they are not already) for the formatting based on date\n",
"df_PendingReport['S.O. Date'] = pd.to_datetime(df_PendingReport['S.O. Date'], errors='coerce')\n",
"df_PendingReport['Rel Date'] = pd.to_datetime(df_PendingReport['Rel Date'], errors='coerce')\n",
"\n",
"# Define colors\n",
"light_green = PatternFill(start_color='EBF1DE', end_color='EBF1DE', fill_type='solid')\n",
"light_orange = PatternFill(start_color='FDE9D9', end_color='FDE9D9', fill_type='solid')\n",
"\n",
"# Get today's date\n",
"today = datetime.today().date()\n",
"\n",
"# Apply formatting based on 'Rel Date'\n",
"for row in range(len(df_PendingReport)): # Iterate over the DataFrame rows\n",
" rel_date_value = df_PendingReport.iloc[row]['Rel Date'] # Access the 'Rel Date'\n",
"\n",
" if isinstance(rel_date_value, pd.Timestamp):\n",
" rel_date_value_date = rel_date_value.date() # Convert to date for comparison\n",
"\n",
" # Apply the formatting logic\n",
" for col in range(3, 13): # Columns C (3) to L (12)\n",
" cell = sheet_PendingReport.cell(row=row + 2, column=col) # Adjust row index for Excel\n",
"\n",
" if rel_date_value_date > today:\n",
" cell.fill = light_green\n",
" elif rel_date_value_date < today:\n",
" cell.fill = light_orange\n",
"\n",
"#####################################################\n",
"# Formating on DO/DX Order ['S.O. #'] \n",
"####################################################\n",
"# Define the fill color for 'Order' column\n",
"light_purple_fill = PatternFill(start_color='E4DFEC', end_color='E4DFEC', fill_type='solid')\n",
"\n",
"# Iterate through rows and fill 'S.O. #' column based on condition\n",
"for row in range(2, sheet_PendingReport.max_row + 1):\n",
" order_cell = sheet_PendingReport.cell(row=row, column=5) # Column E (1-based index)\n",
" if order_cell.value and str(order_cell.value).startswith('D'):\n",
" order_cell.fill = light_purple_fill\n",
"\n",
"#################################################################################################################\n",
"# Formating ['Pty Indice'], ['Where Used Top Level'] and ['Action Needed] in bold with thick left and right border\n",
"####################################################################################################################\n",
"# Define the column indices for 'Action Needed', column C, and column A\n",
"action_needed_col_index = 12 # Column L\n",
"column_c_index = 3 # Column C\n",
"column_a_index = 1 # Column A\n",
"\n",
"# Define the thick border style for left and right\n",
"thick_left_right_border = Border(\n",
" left=Side(style='thick'),\n",
" right=Side(style='thick')\n",
")\n",
"\n",
"# Function to apply formatting\n",
"def apply_formatting(column_index):\n",
" for row in range(2, sheet_PendingReport.max_row + 1): # Starting from row 2\n",
" cell = sheet_PendingReport.cell(row=row, column=column_index)\n",
" cell.font = Font(bold=True)\n",
" \n",
" # Preserve existing top and bottom borders\n",
" current_border = cell.border\n",
" cell.border = Border(\n",
" left=thick_left_right_border.left,\n",
" right=thick_left_right_border.right,\n",
" top=current_border.top,\n",
" bottom=current_border.bottom\n",
" )\n",
"\n",
"# Apply formatting to columns A, C, and L\n",
"apply_formatting(action_needed_col_index)\n",
"apply_formatting(column_c_index)\n",
"apply_formatting(column_a_index)\n",
"\n",
"##################################################################################################\n",
"# Color formating based on mapping from |CM_Priority| on df_PendingReport ['Production Status']\n",
"##################################################################################################\n",
"#Color already defined in BACKLOG section\n",
"col_idd_top_level_PendingReport = [cell.value for cell in sheet_PendingReport[1]].index(\"IDD Top Level\") + 1\n",
"\n",
"# Using the mapping of IDD Top Level values to their respective formatting in the Priority sheet (see section BACKLOG)\n",
"# Apply the formatting to the PendingReport sheet based on the mappings\n",
"# Define column indices for A and X (1-based index)\n",
"col_A_index = 1 # Column A is the 1st column (1-based index)\n",
"col_X_index = 24 # Column X is the 24th column (1-based index)\n",
"\n",
"# Convert to zero-based indices for Python list handling\n",
"col_A_index_zero_based = col_A_index - 1\n",
"col_X_index_zero_based = col_X_index - 1\n",
"\n",
"# Iterate over rows starting from the second row (assuming the first row is headers)\n",
"for row in sheet_PendingReport.iter_rows(min_row=2, max_row=sheet_PendingReport.max_row):\n",
" idd_value = row[col_idd_top_level_PendingReport - 1].value # Adjust for zero-based index\n",
" \n",
" if idd_value in formatting_map:\n",
" fill = formatting_map[idd_value]\n",
" # Apply formatting only to columns A and X\n",
" if row[col_A_index_zero_based].value is not None:\n",
" row[col_A_index_zero_based].fill = fill\n",
" row[col_A_index_zero_based].font = font_color\n",
" \n",
" if row[col_X_index_zero_based].value is not None:\n",
" row[col_X_index_zero_based].fill = fill\n",
" row[col_X_index_zero_based].font = font_color\n",
"\n",
" if idd_value in completed_map:\n",
" fill, font = completed_map[idd_value]\n",
" # Apply formatting only to columns A and X\n",
" if row[col_A_index_zero_based].value is not None:\n",
" row[col_A_index_zero_based].fill = fill\n",
" row[col_A_index_zero_based].font = font\n",
" \n",
" if row[col_X_index_zero_based].value is not None:\n",
" row[col_X_index_zero_based].fill = fill\n",
" row[col_X_index_zero_based].font = font\n",
"\n",
"################################################################\n",
"# Save the updated workbook\n",
"################################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Pending Report added successfully as |PendingReport| in {original_input}\")\n",
"\n",
"#***************************************************************************************************************************\n",
"######################################################################################################################################################################################\n",
"## ## ## ###### ######## ########## ######### ######### ###### #########\n",
"## ## ## ## ## ## ## ## ## ## ## ##\n",
"## ######## ## ######## ## ## ## ######### ## ##\n",
"## ## ## ## ## ## ## ## ## ### ## ##\n",
"## ## ## ###### ######## ## ######### ## ### ###### #########\n",
"######################################################################################################################################################################################\n",
"#***************************************************************************************************************************\n",
"#***************************************************************************************************************************\n",
"# Define date and path\n",
"input_historic_formatted = os.path.join(Path, f'CM_IDD_TurnoverReport_Historic_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |Historic| ...')\n",
"\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_Historic = pd.read_excel(input_historic_formatted, sheet_name=0)\n",
" #print(\"Pending Report files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input PendingReport file not found: {e}\")\n",
" exit()\n",
"\n",
"################################################\n",
"# Formatting 'Tracking#'\n",
"#################################################\n",
"# Replace NaN with empty string\n",
"df_Historic['Tracking#'] = df_Historic['Tracking#'].fillna('')\n",
"\n",
"# Convert 'Tracking#' column to string\n",
"df_Historic['Tracking#'] = df_Historic['Tracking#'].astype(str)\n",
"\n",
"# Remove '.0' from the string if it exists\n",
"df_Historic['Tracking#'] = df_Historic['Tracking#'].str.replace('.0', '', regex=False)\n",
"\n",
"#######################################\n",
"# Create a column 'IDD Marge Standard'\n",
"######################################\n",
"#Fill the blank\n",
"''' Update 09/23 to avoid warning\n",
"df_Historic['Currency turnover ex.VAT'].fillna(0, inplace=True)\n",
"df_Historic['Standard amount USD'].fillna(0, inplace=True)\n",
"'''\n",
"df_Historic['Currency turnover ex.VAT'] = df_Historic['Currency turnover ex.VAT'].fillna(0)\n",
"df_Historic['Standard amount USD'] = df_Historic['Standard amount USD'].fillna(0)\n",
"\n",
"#Format to numeric \n",
"df_Historic['Currency turnover ex.VAT'] = pd.to_numeric(df_Historic['Currency turnover ex.VAT'], errors='coerce')\n",
"df_Historic['Standard amount USD'] = pd.to_numeric(df_Historic['Standard amount USD'], errors='coerce')\n",
"\n",
"#Calculation of the marge standard \n",
"df_Historic['IDD Marge Standard'] = df_Historic['Currency turnover ex.VAT'] - df_Historic['Standard amount USD']\n",
"\n",
"#############################\n",
"# Creating column 'Month'\n",
"############################\n",
"# Ensure 'Invoice date' is in datetime format\n",
"df_Historic['Invoice date'] = pd.to_datetime(df_Historic['Invoice date'])\n",
"\n",
"# Directly create 'Month' in the desired format (e.g., Jan 23, Feb 23, ...)\n",
"df_Historic['Month'] = df_Historic['Invoice date'].dt.strftime('%b %y')\n",
"\n",
"# Convert 'Invoice date' back to short date format (e.g., MM/DD/YYYY)\n",
"df_Historic['Invoice date'] = df_Historic['Invoice date'].dt.strftime('%m/%d/%Y')\n",
"#\n",
"##########################################################################################################################################\n",
"# Creating column 'Complexity' with the function to define the familly based on 'Description' + new function to assign a complecity level\n",
"###########################################################################################################################################\n",
"# Define the 'Product Category' base on 'Description' \n",
"#######################################################\n",
"# Define the 'Product Category' based on 'General Description'\n",
"def determine_category(description):\n",
" if not isinstance(description, str):\n",
" return 'Others'\n",
" if description == 'Rototellite':\n",
" return 'Rototellite'\n",
" elif 'Indicator' in description or 'CPA' in description:\n",
" return 'CPA'\n",
" elif 'Lightplate' in description:\n",
" return 'Lightplate'\n",
" elif 'ISP' in description or 'Keyboard' in description:\n",
" return 'ISP'\n",
" elif 'Module' in description:\n",
" return 'CPA'\n",
" elif 'optics' in description:\n",
" return 'Fiber Optics'\n",
" else:\n",
" return 'Others'\n",
"\n",
"# Create 'Product Category' column based on the 'Description'\n",
"df_Historic['Product Category'] = df_Historic['Description'].apply(determine_category)\n",
"\n",
"###################################################\n",
"# Assigne a complexity Level base on the familly \n",
"##################################################\n",
"# 'Kit' = 0, 'Rototellite' and 'Lightplete' = 1, 'CPA' = 2, 'ISP' = 3\n",
"# Assign a complexity level based on the category\n",
"def determine_complexity(category):\n",
" if not isinstance(category, str):\n",
" return 0\n",
" if category == 'Rototellite':\n",
" return 0.50\n",
" elif category == 'Lightplate':\n",
" return 0.25\n",
" elif category == 'CPA':\n",
" return 0.75\n",
" elif category == 'ISP':\n",
" return 1\n",
" elif category == 'Fiber Optics':\n",
" return 0.50\n",
" elif category == 'Others':\n",
" return 0\n",
" else:\n",
" return 0\n",
" \n",
"# Create 'Complexity' column based on 'Product Category'\n",
"df_Historic['Complexity'] = df_Historic['Product Category'].apply(determine_complexity)\n",
"\n",
"#print('df_Historic')\n",
"#display(df_Historic)\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating |Historic| #########################\n",
"###################################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if 'Historic' in workbook.sheetnames:\n",
" del workbook['Historic']\n",
"\n",
"# Create new \"CM-TurnoverReport\" sheet\n",
"sheet_Historic = workbook.create_sheet(title='Historic')\n",
"\n",
"# Write headers to Excel\n",
"for c_idx, header in enumerate(df_Historic.columns, start=1):\n",
" sheet_Historic.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data to Excel\n",
"for r_idx, row in enumerate(df_Historic.values, start=2):\n",
" for c_idx, value in enumerate(row, start=1):\n",
" sheet_Historic.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"###############################################################################################################\n",
"################################################ Formatting |Historic| ########################################\n",
"###############################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_Historic[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_Historic.auto_filter.ref = sheet_Historic.dimensions\n",
"\n",
"##########################################################\n",
"# Set column widths and text alignment for columns A to V\n",
"#############################################################\n",
"for column in sheet_Historic.iter_cols(min_col=1, max_col=21):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['A', 'B', 'F', 'G', 'M', 'P', 'I', 'Q', 'R', 'U']:\n",
" sheet_Historic.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['H', 'L', 'N']:\n",
" sheet_Historic.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['C', 'D', 'N', 'S', 'T']:\n",
" sheet_Historic.column_dimensions[column_letter].width = 20\n",
" else:\n",
" sheet_Historic.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Apply borders for rows 1 and 2 in column V\n",
"for row in range(1, 3):\n",
" sheet_Historic.cell(row=row, column=22).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set the width of column V and Y\n",
"sheet_Historic.column_dimensions['V'].width = 15\n",
"sheet_Historic.column_dimensions['Y'].width = 20\n",
"\n",
"# Set background color for cell V2 and V3 left align\n",
"sheet_Historic.cell(row=2, column=22).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"sheet_Historic.cell(row=2, column=22).alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"############################################################################\n",
"# Set column widths and text alignment for columns after V --> W to Z\n",
"############################################################################\n",
"for column in sheet_Historic.iter_cols(min_col=23, max_col=26):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" \n",
" cell.border = Border(\n",
" top=Side(style='thin'),\n",
" right=Side(style='thin'),\n",
" bottom=Side(style='thin'),\n",
" left=Side(style='thin')\n",
" )\n",
" \n",
"##################################################################################################\n",
"# Color formating based on mapping from |CM_Priority| on df_Historic ['Production Status']\n",
"##################################################################################################\n",
"gray_fill = PatternFill(start_color='F2F2F2', end_color='DAEEF3', fill_type='solid')\n",
"\n",
"# Create a mapping of IDD Top Level values to their respective formatting\n",
"formatting_map = {}\n",
"completed_map = {}\n",
"\n",
"# Populate formatting_map based on 'Production Status'\n",
"for row in sheet_Historic.iter_rows(min_row=2, max_row=sheet_Historic.max_row):\n",
" idd_value = row[2].value # Column C (3rd column, zero-based index 2)\n",
" production_status = row[19].value # Column T (20th column, zero-based index 19)\n",
"\n",
" if production_status == 'Industrialized':\n",
" formatting_map[idd_value] = (green_fill,)\n",
" elif 'WIP' in str(production_status):\n",
" formatting_map[idd_value] = (blue_fill,)\n",
" elif production_status == 'Completed':\n",
" formatting_map[idd_value] = (gray_fill,)\n",
"\n",
"# Apply the formatting based on formatting_map\n",
"start_col_index_zero_based = 0 # Column A (0-based index)\n",
"end_col_index_zero_based = 20 # Column U (0-based index)\n",
"\n",
"for row in sheet_Historic.iter_rows(min_row=2, max_row=sheet_Historic.max_row):\n",
" idd_value = row[2].value # Column C (zero-based index 2)\n",
"\n",
" if idd_value in formatting_map:\n",
" fill = formatting_map[idd_value][0]\n",
" for col_index in range(start_col_index_zero_based, end_col_index_zero_based + 1):\n",
" if row[col_index].value is not None: # Ensure cell is not empty\n",
" row[col_index].fill = fill\n",
"\n",
"############################\n",
"# Higlight NC order in yellow for column J 'Order'\n",
"#############################\n",
"# Highlight NC orders in yellow for entire row when 'Order' contains 'NC'\n",
"yellow_fill = PatternFill(start_color='FFFF99', end_color='FFFF99', fill_type='solid')\n",
"\n",
"# Iterate through rows starting from the second row (index 1 in Python)\n",
"for row in range(2, sheet_Historic.max_row + 1):\n",
" order_value = sheet_Historic.cell(row=row, column=10).value # Assuming 'Order' is in column J (10th column)\n",
" if order_value and 'NC' in str(order_value): # Check if 'NC' is in the 'Order' cell\n",
" # Iterate through all cells in the current row (columns A to V)\n",
" for col in range(1, 22): # Adjust max_col based on your actual number of columns\n",
" cell = sheet_Historic.cell(row=row, column=col)\n",
" cell.fill = yellow_fill\n",
"\n",
"#####################################################\n",
"# Formating on DO/DX Order on ['Order'] \n",
"####################################################\n",
"# Iterate through rows and fill 'Order' column based on condition\n",
"for row in range(2, sheet_Historic.max_row + 1):\n",
" order_cell = sheet_Historic.cell(row=row, column=9) # Column J (1-based index)\n",
" if order_cell.value and str(order_cell.value).startswith('D'):\n",
" order_cell.fill = light_purple_fill\n",
"\n",
"################################################################\n",
"# Rename the sheet\n",
"sheet_Historic.title = 'CM-Historic'\n",
"\n",
"#### NOTE ON 07/31 -->> Gantt is still the workbook active here \n",
"\n",
"################################################\n",
"# Save the updated workbook\n",
"################################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Historic added successfully as |CM-Historic| in {original_input}\")\n",
"\n",
"# -->> update 08/27\n",
"#///////////////////////////////////////////\n",
"#####################################################################################################\n",
"## Update ['Shipped'] and ['Remain. crit. Qty']from df_Priority based on df_Historic & df_TurnoverReport \n",
"###################################################################################################\n",
"#//////////////////////////////////////////\n",
"#loaad a copy of df_Priority \n",
"df_Priority_updated = df_Priority.copy()\n",
"\n",
"#Update df_Priority_updated['Shipped'] based on df_Historic['Quantity'] and df_TurnoverReport['TurnoverReport row Qty'] by \n",
"# 1. looking at the dates df_Historic['Invoice date'] & df_TurnoverReport['Invoice date'] \n",
"# 2. looking at the order df_Historic['Order'] & df_TurnoverReport['Order'] \n",
"# 3. Summing for a given Pty Indice df_Historic['Quantity'] & df_TurnoverReport['TurnoverReport row Qty'] ensuring to no compte several time the same line \n",
"\n",
"# Group and summarize the quantities by 'Pty Indice', 'Invoice date', 'Order', and 'Tracking#']\n",
"# For df_Historic\n",
"df_Historic_grouped = df_Historic.groupby(['Pty Indice', 'Invoice date', 'Order', 'Tracking#'])['Quantity'].sum().reset_index()\n",
"\n",
"# For df_TurnoverReport\n",
"df_Turnover_grouped = df_TurnoverReport.groupby(['Pty Indice', 'Invoice date', 'Order', 'Tracking#'])['TurnoverReport row Qty'].sum().reset_index()\n",
"\n",
"# Filter out rows where 'Order' contains 'NC'\n",
"df_Historic_grouped = df_Historic_grouped[~df_Historic_grouped['Order'].str.contains('NC', na=False)]\n",
"df_Turnover_grouped = df_Turnover_grouped[~df_Turnover_grouped['Order'].str.contains('NC', na=False)]\n",
"\n",
"# Concatenate the two dataframes\n",
"combined_df = pd.concat([\n",
" df_Historic_grouped.rename(columns={'Quantity': 'Shipped'}),\n",
" df_Turnover_grouped.rename(columns={'TurnoverReport row Qty': 'Shipped'})\n",
"])\n",
"\n",
"#Update on 08/29 \n",
"# Replace the string 'nan' with an empty string\n",
"''' SAVED 09/24 to avoid warning\n",
"combined_df['Tracking#'].replace('nan', '', inplace=True)\n",
"# Replace empty strings and None with NaN, then fill NaN with 'N/A'\n",
"combined_df['Tracking#'].replace([\"\", None, np.nan], \"N/A\", inplace=True)\n",
"'''\n",
"combined_df['Tracking#'] = combined_df['Tracking#'].replace('nan', '')\n",
"combined_df['Tracking#'] = combined_df['Tracking#'].replace([\"\", None, np.nan], \"N/A\")\n",
"\n",
"\n",
"# Drop duplicates to avoid double counting\n",
"combined_df = combined_df.drop_duplicates(subset=['Pty Indice', 'Invoice date', 'Order', 'Tracking#', 'Shipped'])\n",
"\n",
"#print('combined_df filtered on P5')\n",
"# Filter the DataFrame\n",
"#filtered_combined_df = combined_df[combined_df['Pty Indice'] == 'P5']\n",
"#display(filtered_combined_df) \n",
"\n",
"# Convert 'Shipped' to integers\n",
"combined_df['Shipped'] = combined_df['Shipped'].astype(int)\n",
"\n",
"# Group by 'Pty Indice' to sum 'Shipped' values only (NOT across all combinations of 'Invoice date', 'Order', and 'Tracking#')\n",
"df_Priority_updated = combined_df.groupby('Pty Indice', as_index=False)['Shipped'].sum()\n",
"\n",
"# Convert 'Shipped' to integer\n",
"df_Priority_updated['Shipped'] = df_Priority_updated['Shipped'].astype(int)\n",
"\n",
"#print('df_Priority_updated')\n",
"#display(df_Priority_updated)\n",
"\n",
"# 08/23\n",
"######/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"###########################################################################################################################################################\n",
"##### ------------->>> Modifiction of |CM-Priority| <<<<-----------------------\n",
"##### Mapping of df_Priority_updated['Shipped'] into Excel tab |CM-Priority| only if df_Priority_updated['Shipped'] > existing value df_Priority['Shipped'] \n",
"###########################################################################################################################################################\n",
"######/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"### Updating the Excel sheet ['Shipped'] and ['Remain. crit. Qty']\n",
"######################################################################\n",
"# Load workbook and sheet CM-Priority \n",
"sheet_Priority = workbook['CM-Priority']\n",
"\n",
"# load headers\n",
"header = list(df_Priority.columns) # Convert Index to list\n",
"\n",
"#Updated 08/29\n",
"# Iterate through the rows in df_Priority_updated\n",
"for index, row in df_Priority_updated.iterrows():\n",
" pty_indice = row['Pty Indice']\n",
" new_shipped = row['Shipped']\n",
"\n",
" # Find the corresponding row in the Excel sheet\n",
" for excel_row in sheet_Priority.iter_rows(min_row=2, max_row=sheet_Priority.max_row, values_only=False):\n",
" if excel_row[header.index('Pty Indice')].value == pty_indice:\n",
" # Update the 'Shipped' value\n",
" excel_row[header.index('Shipped')].value = new_shipped\n",
" \n",
" # Calculate 'Remain. Crit. Qty'\n",
" crit_qty = excel_row[header.index('Critical Qty')].value\n",
" \n",
" # Ensure new_shipped and crit_qty are treated as integers before subtraction\n",
" remain_crit_qty = max(int(crit_qty) - int(new_shipped), 0)\n",
"\n",
" # Explicitly set the value as an integer\n",
" remain_crit_qty_cell = excel_row[header.index('Remain. crit. Qty')]\n",
" remain_crit_qty_cell.value = remain_crit_qty\n",
" remain_crit_qty_cell.number_format = '0' # Format the cell to show integers only\n",
"\n",
" break\n",
"\n",
"########################################################################\n",
"#### Apply formating on CM-Priority after modifying the sheet \n",
"##########################################################################\n",
"# Get the maximum row number\n",
"max_row = sheet_Priority.max_row\n",
"\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_Priority[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_Priority.auto_filter.ref = sheet_Priority.dimensions\n",
"\n",
"############################################################\n",
"# Set column widths and text alignment for columns A to T\n",
"##############################################################\n",
"for column in sheet_Priority.iter_cols(min_col=1, max_col=20):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['B', 'C', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'R', 'T']:\n",
" sheet_Priority.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['A', 'E']:\n",
" sheet_Priority.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['D', 'Q', 'O', 'P']:\n",
" sheet_Priority.column_dimensions[column_letter].width = 35\n",
" elif column_letter in ['Q']:\n",
" sheet_Priority.column_dimensions[column_letter].width = 50\n",
" else:\n",
" sheet_Priority.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"############################################################\n",
"# Set column widths and text alignment for columns V to Z\n",
"##############################################################\n",
"for column in sheet_Priority.iter_cols(min_col=22, max_col=26):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['V', 'W', 'X', 'Y', 'Z']:\n",
" sheet_Priority.column_dimensions[column_letter].width = 20\n",
" else:\n",
" sheet_Priority.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"##############################################################\n",
"# Set column width and text alignment for column U from row 3\n",
"#################################################################\n",
"for cell in sheet_Priority.iter_rows(min_row=3, min_col=21, max_col=21):\n",
" cell[0].alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Set the width of column U to 15mm\n",
"column_letter = get_column_letter(21) # Column T is the 21th column\n",
"sheet_Priority.column_dimensions[column_letter].width = 15\n",
"\n",
"# Apply borders for rows 1 and 2 in column U\n",
"for row in range(1, 3):\n",
" sheet_Priority.cell(row=row, column=21).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell U2 and U3 left align\n",
"sheet_Priority.cell(row=2, column=21).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"# Format cell U2 as a short date\n",
"sheet_Priority.cell(row=2, column=21).number_format = 'mm-dd-yyyy'\n",
"\n",
"# Set short date format for columns K, L, N and M starting from row 2 up to max_row\n",
"for column_letter in ['L', 'M', 'N']:\n",
" for row in range(2, sheet_Priority.max_row + 1):\n",
" cell = sheet_Priority[f'{column_letter}{row}']\n",
" cell.number_format = 'mm-dd-yyyy'\n",
"\n",
"###############################\n",
"#### Industrialized and WIP PN\n",
"###############################\n",
"# Define the fill for the conditional formatting\n",
"green_fill = PatternFill(start_color='D8E4BC', end_color='D8E4BC', fill_type='solid')\n",
"blue_fill = PatternFill(start_color='DAEEF3', end_color='DAEEF3', fill_type='solid')\n",
"\n",
"# Define the font color\n",
"font_color = Font(color='000000') # Black font color\n",
"\n",
"# Iterate over each row in column O\n",
"for row in range(2, max_row + 1): # Start from row 2 since row 1 contains headers\n",
" # Get the value in the current cell in column O\n",
" value_in_o = sheet_Priority.cell(row=row, column=15).value # Column O is the 15th column (1-based index)\n",
" \n",
" # Determine the fill color based on the value in column O\n",
" if value_in_o == 'Industrialized':\n",
" fill_color = green_fill\n",
" elif 'WIP' in str(value_in_o):\n",
" fill_color = blue_fill\n",
" else:\n",
" fill_color = None\n",
" \n",
" # Apply the fill and font color to columns A to T and V to Z\n",
" if fill_color:\n",
" for col in list(range(1, 21)) + list(range(22, 27)): # Columns A-T (1-20) and V-Z (22-26)\n",
" cell = sheet_Priority.cell(row=row, column=col)\n",
" cell.fill = fill_color\n",
" cell.font = font_color\n",
" \n",
"###################################\n",
"#### SO Modified red if pushed-out to 12/31/2026 or 12/24/2026\n",
"##################################\n",
"# Define the font color for dark red\n",
"dark_red_font = Font(color='C00000')\n",
"\n",
"# Define the fill color for light yellow\n",
"light_yellow_fill = PatternFill(start_color='FFFFCC', end_color='FFFFCC', fill_type='solid')\n",
"\n",
"# Get the maximum row number in column M (SO Modified)\n",
"max_row_M = sheet_Priority.max_row\n",
"\n",
"# Define the target dates as strings\n",
"target_dates = ['12/31/2026', '12/24/2026']\n",
"\n",
"# Iterate over each row in column M\n",
"max_row_M = sheet_Priority.max_row\n",
"for row in range(2, max_row_M + 1): # Start from row 2 since row 1 contains headers\n",
" # Get the value in the current cell in column M\n",
" value_in_m = sheet_Priority.cell(row=row, column=13).value # Column M is the 13th column\n",
" \n",
" # Check if the value in column M matches the target dates\n",
" if value_in_m in target_dates:\n",
" # Apply the light yellow fill and dark red font color to the cell\n",
" cell = sheet_Priority.cell(row=row, column=13)\n",
" cell.fill = light_yellow_fill\n",
" cell.font = dark_red_font\n",
"\n",
"############################################################################################\n",
"#### Redlist - If |Production Status| contain something else then a number or 'TBD' apply red fill\n",
"############################################################################################\n",
"# Define the fill for the conditional formatting\n",
"red_fill = PatternFill(start_color='F2DCDB', end_color='F2DCDB', fill_type='solid')\n",
"border_color = '000000' # Black color for the border\n",
"\n",
"# Define thin black border style for top and bottom sides\n",
"thin_black_side = Side(style='thin', color=border_color)\n",
"\n",
"# Define diagonal cross-border style (grey color)\n",
"border_grey = Border(\n",
" left=Side(border_style=None),\n",
" right=Side(border_style=None),\n",
" top=Side(border_style=None),\n",
" bottom=Side(border_style=None),\n",
" diagonal=Side(border_style='thin', color='D9D9D9'),\n",
" diagonalDown=True,\n",
" diagonalUp=True\n",
")\n",
"\n",
"# Iterate over each row\n",
"for row in range(2, max_row + 1): # Start from row 2 since row 1 contains headers\n",
" # Get the value in column O\n",
" value_in_O = sheet_Priority.cell(row=row, column=15).value # Column O is the 15th column (1-based index)\n",
"\n",
" # Check if the value in column O contains 'Transfer' or 'Canceled'\n",
" if value_in_O and ('transferred' in str(value_in_O).lower() or 'canceled' in str(value_in_O).lower()):\n",
" # Apply red fill and border styles to columns A to T and V to Z\n",
" for col in list(range(1, 21)) + list(range(22, 27)): # Columns A-T (1-20) and V-Z (22-26)\n",
" cell = sheet_Priority.cell(row=row, column=col)\n",
" cell.fill = red_fill\n",
"\n",
" # Apply border styles\n",
" if col == 1 or col == sheet_Priority.max_column:\n",
" # Apply black border to leftmost and rightmost columns\n",
" cell.border = Border(\n",
" left=thin_black_side if col == 1 else None,\n",
" right=thin_black_side if col == sheet_Priority.max_column else None,\n",
" top=thin_black_side,\n",
" bottom=thin_black_side,\n",
" diagonal=border_grey.diagonal,\n",
" diagonalDown=border_grey.diagonalDown,\n",
" diagonalUp=border_grey.diagonalUp\n",
" )\n",
" else:\n",
" # Apply grey diagonal cross-border to other columns\n",
" cell.border = border_grey\n",
"\n",
"###########################\n",
"#### Completed PN\n",
"###########################\n",
"# Define the fill for the conditional formatting\n",
"grey_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')\n",
"\n",
"# Define the font color\n",
"grey_font_color = Font(color='BFBFBF')\n",
"\n",
"# Iterate over each row in column I\n",
"for row in range(2, max_row + 1): # Start from row 2 since row 1 contains headers\n",
" # Get the value in the current cell in column I\n",
" value_in_i = sheet_Priority.cell(row=row, column=9).value # Column I is the 9th column (1-based index)\n",
" \n",
" # Check if the value in column I is 'Completed'\n",
" if value_in_i == 'Completed':\n",
" # Apply the grey fill and font color to columns A-T and V-Z\n",
" for col in list(range(1, 21)) + list(range(22, 27)): # Columns A-T (1-20) and V-Z (22-26)\n",
" cell = sheet_Priority.cell(row=row, column=col)\n",
" cell.fill = grey_fill\n",
" cell.font = grey_font_color\n",
"\n",
"###########################\n",
"#### 'Pty Indice', 'Qty clear to build' & 'Production status' Bold and font color formatting\n",
"###########################\n",
"# Define the font for bold text\n",
"bold_font = Font(bold=True)\n",
"\n",
"# Define font colors\n",
"font_color_industrialized = \"008000\" # Dark Green\n",
"font_color_completed = \"808080\" # Grey\n",
"font_color_wip = \"1F497D\" # Dark Blue\n",
"font_color_transfer = \"C00000\" # Dark Red\n",
"\n",
"# Iterate over each row\n",
"for row in range(2, max_row + 1): # Assuming max_row is already defined\n",
" # Get the cells in columns E, F, and O for the current row\n",
" cell_E = sheet_Priority.cell(row=row, column=5) # Column E is the 5th column\n",
" cell_F = sheet_Priority.cell(row=row, column=6) # Column F is the 6th column\n",
" cell_O = sheet_Priority.cell(row=row, column=15) # Column O is the 15th column\n",
" \n",
" # Set the font to bold for cells in columns E and O\n",
" cell_E.font = bold_font\n",
" cell_O.font = bold_font\n",
" \n",
" # Convert cell_O.value to a string to handle NoneType values\n",
" cell_O_value_str = str(cell_O.value)\n",
" \n",
" # Set the font color for column E based on the value in column O\n",
" if cell_O_value_str == \"Industrialized\":\n",
" cell_E.font = Font(color=font_color_industrialized, bold=True)\n",
" elif cell_O_value_str == \"Completed\":\n",
" cell_E.font = Font(color=font_color_completed, bold=True)\n",
" elif \"WIP\" in cell_O_value_str: # Checking if \"WIP\" is contained in the value\n",
" cell_E.font = Font(color=font_color_wip, bold=True)\n",
" elif \"transferred\" in cell_O_value_str or \"Canceled\" in cell_O_value_str:\n",
" cell_E.font = Font(color=font_color_transfer, bold=True)\n",
" \n",
" # Set the font color for column O to match column E\n",
" cell_O.font = Font(color=cell_E.font.color, bold=True)\n",
" \n",
"###########################\n",
"#### Applied thick border to |Pty Indice|Clear to build| ... |Production Status|\n",
"###########################\n",
"# Define thick border style for left and right sides\n",
"thick_side = Side(style='thick')\n",
"\n",
"# Define thin black border style for top and bottom sides\n",
"thin_black_side = Side(style='thin', color='000000')\n",
"\n",
"# Define the column indices for the range\n",
"column_indices = [5, 6, 15] # Columns E, F, and O\n",
"\n",
"# Iterate over each row and apply the defined border style to the specified columns\n",
"for row in range(2, max_row + 1): # Assuming max_row is already defined\n",
" for col_index in column_indices:\n",
" cell = sheet_Priority.cell(row=row, column=col_index)\n",
" cell.border = Border(\n",
" left=thick_side,\n",
" right=thick_side,\n",
" top=thin_black_side,\n",
" bottom=thin_black_side\n",
" )\n",
"\n",
"#####################################################################\n",
"### Save the changes to the Excel file \n",
"#####################################################################\n",
"# Save and close workbook\n",
"workbook.save(original_input)\n",
"workbook.close()\n",
"\n",
"#***************************************************************************************************************************\n",
"############################################################################################################################\n",
"## ## ############ ########### ############# ############## \n",
"## ## ## ## ## ## ## ## ## ##\n",
"## ## ## ## ## ## ## ## ## ##\n",
"## ## ############ ########### ## ## ##############\n",
"## ## ## ## ## ## ## ## ## ###\n",
"## ## ## ## ## ## ## ## ## ###\n",
"## ######## ## ## ########### ############# ## ###\n",
"############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"#***************************************************************************************************************************\n",
"# Define date and path\n",
"input_LaborReport_formatted = os.path.join(Path, f'CM_IDD_LaborReport_Historic_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |CM-LaborReport| ...')\n",
"\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_LaborReport = pd.read_excel(input_LaborReport_formatted, sheet_name=0)\n",
" #print(\"Pending Report files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input PendingReport file not found: {e}\")\n",
" exit()\n",
"\n",
"######################################################\n",
"# Create column 'Actual vs Expected (%)'\n",
"########################################################\n",
"# Convert ['Actual Time (hours)'] will set 'Inaccurate data' to NaN\n",
"df_LaborReport['Actual Time (hours)'] = pd.to_numeric(df_LaborReport['Actual Time (hours)'], errors='coerce')\n",
"\n",
"#Drop NaN values \n",
"df_LaborReport.dropna(subset=['Actual Time (hours)', 'Expected Time (hours)'], inplace=True)\n",
"\n",
"# Calculation \n",
"df_LaborReport['Actual vs Expected (%)'] = (df_LaborReport['Actual Time (hours)'] / df_LaborReport['Expected Time (hours)']) * 100\n",
"df_LaborReport['Actual vs Expected (%)'] = df_LaborReport['Actual vs Expected (%)'].round(2)\n",
"\n",
"# Convert to string and append '%' symbol\n",
"df_LaborReport['Actual vs Expected (%)'] = df_LaborReport['Actual vs Expected (%)'].astype(str) + '%'\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating |CM-LaborReport| ####################\n",
"###################################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if 'CM-LaborReport' in workbook.sheetnames:\n",
" del workbook['CM-LaborReport']\n",
"\n",
"# Create new \"CM-LaborReport\" sheet\n",
"sheet_LaborReport = workbook.create_sheet(title='CM-LaborReport')\n",
"\n",
"# Write headers to Excel\n",
"for c_idx, header in enumerate(df_LaborReport.columns, start=1):\n",
" sheet_LaborReport.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data to Excel\n",
"for r_idx, row in enumerate(df_LaborReport.values, start=2):\n",
" for c_idx, value in enumerate(row, start=1):\n",
" sheet_LaborReport.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"###############################################################################################################\n",
"################################################ Formatting |CM-LaborReport| #################################\n",
"###############################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_LaborReport[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_LaborReport.auto_filter.ref = sheet_LaborReport.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to U\n",
"for column in sheet_LaborReport.iter_cols(min_col=1, max_col=20):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['A', 'B', 'F']:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['D']:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['C', 'I', 'J', 'K']:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 20\n",
" else:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
"# Apply borders for rows 1 and 2 in column U\n",
"for row in range(1, 3):\n",
" sheet_LaborReport.cell(row=row, column=21).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Set background color for cell U2 and U3 left align\n",
"sheet_LaborReport.cell(row=2, column=21).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"sheet_LaborReport.cell(row=2, column=21).alignment = Alignment(horizontal='left', vertical='center')\n",
" \n",
"########################################################\n",
"# Higlight NC order in yellow for column F 'WO NR'\n",
"##########################################################\n",
"#Update 08/26\n",
"# Highlight NC orders in yellow for entire row when 'Order' contains 'NC'\n",
"yellow_fill = PatternFill(start_color='FFFF99', end_color='FFFF99', fill_type='solid')\n",
"\n",
"# Iterate through rows starting from the second row (index 1 in Python)\n",
"for row in range(2, sheet_LaborReport.max_row + 1):\n",
" order_value = sheet_LaborReport.cell(row=row, column=6).value # Assuming 'Order' is in column F (6th column)\n",
" if order_value and 'NC' in str(order_value).upper(): # Convert to uppercase and check if 'NC' is in the 'Order' cell\n",
" # Iterate through all cells in the current row (columns A to T)\n",
" for col in range(1, 21): # Adjust max_col based on your actual number of columns\n",
" cell = sheet_LaborReport.cell(row=row, column=col)\n",
" cell.fill = yellow_fill\n",
"\n",
"###############################################\n",
"# Coloring column 'Level' \n",
"################################################\n",
"# Custom conditional formatting for column J 'Level' (Assuming data starts from row 2)\n",
"min_row = 2\n",
"max_row = sheet_LaborReport.max_row\n",
"col_E = 5\n",
"\n",
"for row in range(min_row, max_row + 1):\n",
" cell_value = sheet_LaborReport.cell(row=row, column=col_E).value\n",
"\n",
" # Define default fill color in case cell_value is not in expected range\n",
" fill_color = None\n",
" \n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" \n",
" # Create PatternFill object only if fill_color is defined\n",
" if fill_color:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_LaborReport.cell(row=row, column=col_E).fill = fill\n",
"\n",
"###########################################################\n",
"# Set column widths and text alignment for columns V to AB\n",
"############################################################\n",
"# set width of U\n",
"sheet_LaborReport.column_dimensions['U'].width = 15\n",
"\n",
"# Set column widths and text alignment for columns V to AA\n",
"for column in sheet_LaborReport.iter_cols(min_col=22, max_col=28):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['U', 'V']:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['Y']:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['W', 'X', 'Z', 'AA', 'AB']:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 20\n",
" else:\n",
" sheet_LaborReport.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"\n",
"#########################################################################################################################\n",
"# Higlight in orange or bleu row related to Prototype and FAI unit --> Those row are filtered-out from the the 'Avg Actual Time'\n",
"# Higlight in gray row related to 'Phantom' part --> row where ['Phantom'] = 'Oui'\n",
"# If sheet_LaborReport['Remarks'] contain 'FA' or 'PROTO' higlitgh row in orange #FCE4D6\n",
"#########################################################################################################################\n",
"# Updated 08/28\n",
"# Define the fill for highlighting\n",
"orange_fill = PatternFill(start_color='FCE4D6', end_color='FCE4D6', fill_type='solid')\n",
"blue_fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid')\n",
"gray_fill = PatternFill(start_color='D3D3D3', end_color='D3D3D3', fill_type='solid')\n",
"\n",
"# Iterate through rows starting from the second row\n",
"for row in range(2, sheet_LaborReport.max_row + 1):\n",
" remarks_value = sheet_LaborReport.cell(row=row, column=25).value # Assuming 'Remarks' is in column Y (25th column)\n",
" phantom_value = sheet_LaborReport.cell(row=row, column=13).value # Assuming 'Phamtom' is in column M (13th column)\n",
"\n",
" if phantom_value and phantom_value.lower() == 'oui':\n",
" fill = gray_fill\n",
" elif remarks_value:\n",
" remarks_value_lower = str(remarks_value).lower()\n",
"\n",
" if 'proto' in remarks_value_lower:\n",
" fill = orange_fill\n",
" elif 'fa' in remarks_value_lower:\n",
" fill = blue_fill\n",
" else:\n",
" continue # Skip rows without \"PROTO\" or \"FA\"\n",
"\n",
" else:\n",
" continue # Skip rows with no relevant remarks or phantom status\n",
"\n",
" # Apply the fill color to all cells in the current row (columns A to T), except column E\n",
" for col in range(1, 21): # Adjust max_col based on your actual number of columns\n",
" if col != 5: # Skip column E (5th column)\n",
" cell = sheet_LaborReport.cell(row=row, column=col)\n",
" cell.fill = fill\n",
"\n",
"################################################\n",
"# Save the updated workbook\n",
"################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"LaborReport added successfully as |CM-LaborReport| in {original_input}\")\n",
"\n",
"\n",
"#New 08/21\n",
"#***************************************************************************************************************************\n",
"############################################################################################################################\n",
"## #### #### ############ \n",
"## ## ## ## ## ## ## \n",
"## ## ## ## ## ## ## \n",
"## ## # ## ############ \n",
"## ## ## ## ## \n",
"## ## ## ## ## \n",
"## ## ##Ake ## ## Rchitecture\n",
"############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"#***************************************************************************************************************************\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |CM-MakeArchitecutre| ...')\n",
"\n",
"#update 08/28 to include case incensitive \n",
"# Create df_Make_architure based on |CM-BOM| datyafram df_CM_BOM where M/P/F = 'M'\n",
"df_Make_architure = df_CM_BOM[df_CM_BOM['M/P/F'].str.upper() == 'M']\n",
"\n",
"###########################################################################################################################\n",
"# Create 'Make Architecture' from |CM-BOM| with the Avrage Expected and Average Actual time per unit from |CM-LaborReport|\n",
"###########################################################################################################################\n",
"# Combine the row to keep a single row for a conbinaison of 'Pty Indice' and 'IDD Component' and return the 'Average count WO' \n",
"# sum the Actual and Expected time and create [Actual Time (unit)] and [Expected Time (unit)] \n",
"# Filter-out row where 'Remarks' contain either 'FAI' or 'PROTO' when calculating the Average times to reduce bias\n",
"#############################################################################################################################\n",
"#Update 08/28 \n",
"# Filter out rows where 'Remarks' contain 'PROTO' (case insensitive) - Sometimes a lot of WO are tag FA without a reason, so it might be better to keep the 'FA'\n",
"# and rows where 'WO#' contain 'NC' (case insensitive)\n",
"# and rows where 'Phantom' is 'Oui'\n",
"df_LaborReport_filtered = df_LaborReport[\n",
" ~df_LaborReport['Remarks'].fillna('').str.contains('PROTO', case=False) & # Filter out 'PROTO'\n",
" ~df_LaborReport['WO#'].fillna('').str.contains('NC', case=False) & # Filter out 'NC'\n",
" ~(df_LaborReport['Phantom'].fillna('').str.lower() == 'oui') # Filter out 'Phantom' = 'Oui'\n",
"]\n",
"\n",
"# Update 09/20 \n",
"# Filter out rows where 'Actual Time (unit)' & 'Expected Time (unit)' are 0 - Update 09/20 to not filter where 'Expected Time (unit)' = 0\n",
"# df_LaborReport_filtered = df_LaborReport_filtered[(df_LaborReport_filtered['Actual Time (unit)'] > 0) &(df_LaborReport_filtered['Expected Time (unit)'] > 0)]\n",
"df_LaborReport_filtered = df_LaborReport_filtered[(df_LaborReport_filtered['Actual Time (unit)'] > 0)]\n",
"\n",
"# Only keep components where 'ACTUAL_RUN_TIME' > 1 hourQty_Count\n",
"#df_LaborReport_filtered = df_LaborReport_filtered[df_LaborReport_filtered['ACTUAL_RUN_TIME'] > 1]\n",
"\n",
"#############################################################################################################\n",
"# New 09/11 - Erase 'ACTUAL_RUN_TIME' (set to NaN) for abberent values in order to get realistic AVG Actual Time\n",
"#############################################################################################################\n",
"#Define a function to remove aberrant values based on IQR \n",
"# Identify aberrant values: This could be based on statistical measures like z-scores, interquartile range (IQR), or other criteria.\n",
"# Define a threshold for the minimum number of values required\n",
"MIN_VALUES_REQUIRED = 6\n",
"\n",
"# Define a function to remove aberrant values based on IQR\n",
"def remove_aberrant_values(df, column_name):\n",
" Q1 = df[column_name].quantile(0.25)\n",
" Q3 = df[column_name].quantile(0.75)\n",
" IQR = Q3 - Q1\n",
"\n",
" lower_bound = Q1 # set as 25% quantille \n",
" upper_bound = Q3 + 1.5 * IQR\n",
"\n",
" return df[(df[column_name] >= lower_bound) & (df[column_name] <= upper_bound)]\n",
"\n",
"'''\n",
"# Function to process each group\n",
"def process_group(group):\n",
" if len(group) >= MIN_VALUES_REQUIRED:\n",
" return remove_aberrant_values(group, 'ACTUAL_RUN_TIME')\n",
" else:\n",
" return group\n",
"'''\n",
"\n",
"# Function to process each group -> Function to be applied to 'Actual Time (unit)' not 'ACTUAL_RUN_TIME'\n",
"def process_group(group):\n",
" if len(group) >= MIN_VALUES_REQUIRED:\n",
" return remove_aberrant_values(group, 'Actual Time (unit)')\n",
" else:\n",
" return group\n",
" \n",
"# Group by 'Pty Indice', 'IDD Component', and 'Level', and apply the function\n",
"# Group by the desired columns\n",
"grouped = df_LaborReport_filtered.groupby(['Pty Indice', 'IDD Component', 'Level'])\n",
"\n",
"# Apply the function to each group to get df_filtered 'Actual Time (unit)' with aberrant values removed\n",
"# df_filtered = grouped.apply(process_group).reset_index(drop=True) - Replaced 09/24 to avoid warning\n",
"df_filtered = grouped.apply(process_group).reset_index(drop=True).copy()\n",
"\n",
"#############################################################################\n",
"# df_filtered is a filtered df_LaborReport_filtered without aberrant values\n",
"#############################################################################\n",
"# Only keep relevant columns from df_filtered\n",
"df_filtered = df_filtered[['Priority', 'Pty Indice', 'IDD Component', 'Actual Time (unit)', 'Expected Time (unit)', 'WO#', 'WO Qty']]\n",
"\n",
"# Convert to numeric to avoid merging issues - Update 09/11\n",
"#df_LaborReport_filtered['Expected Time (unit)'] = pd.to_numeric(df_LaborReport_filtered['Expected Time (unit)'], errors='coerce')\n",
"#df_LaborReport_filtered['Actual Time (unit)'] = pd.to_numeric(df_LaborReport_filtered['Actual Time (unit)'], errors='coerce')\n",
"# Convert to numeric to avoid merging issues\n",
"df_filtered['Expected Time (unit)'] = pd.to_numeric(df_filtered['Expected Time (unit)'], errors='coerce')\n",
"df_filtered['Actual Time (unit)'] = pd.to_numeric(df_filtered['Actual Time (unit)'], errors='coerce')\n",
"\n",
" # Updated 09/11 to replace df_LaborReport_filtered by df_filtered to erase abberant values for the calculatation of Actual Time (unit)\n",
"# --> |CM-LaborReport| still contain all the values, while |CM-MakeArchitecture| calculation are based on a filtered datafram df_LaborReport_filtered. Apply color formating in red of the row filtered out from the datafram.\n",
"# Perform the merge and keep all columns from both dataframes\n",
"df_Make_architure = df_Make_architure.merge(\n",
" df_filtered,\n",
" on=['IDD Component', 'Pty Indice'],\n",
" how='left',\n",
" suffixes=('', '_from_LaborReport')\n",
")\n",
"\n",
"# Drop any unnecessary columns\n",
"df_Make_architure.drop(columns=['BOM Index_from_LaborReport'], inplace=True, errors='ignore')\n",
"\n",
"#Remove duplicates based on 'WO#' to ensure each 'WO#' is counted only once\n",
"unique_wo_data = df_Make_architure.drop_duplicates(subset='WO#')\n",
"\n",
"# Group by 'Pty Indice' and 'IDD Component' and perform the aggregation\n",
"final_aggregated = unique_wo_data.groupby(['Pty Indice', 'IDD Component']).agg(\n",
" WO_Count=('WO#', 'nunique'), # Count the number of unique 'WO#' for each group\n",
" Qty_Count=('WO Qty', 'sum'), # Sum the 'WO Qty' for each group\n",
" Avg_Expected_Time=('Expected Time (unit)', 'mean'), # Mean of 'Expected Time (unit)'\n",
" Avg_Actual_Time=('Actual Time (unit)', 'mean'), # Mean of 'Actual Time (unit)'\n",
" Var_Actual_Time=('Actual Time (unit)', 'var'), # Variance of 'Actual Time (unit)'\n",
" Max_Expected_Time=('Expected Time (unit)', 'last') # Maximum 'Expected Time (unit)' --> Update 09/16 to get the 'last' instead of the 'max' as the most recent WO with the most updated BOM is the last for a given PN\n",
").reset_index()\n",
"\n",
"# Calculate standard deviation based on variance\n",
"final_aggregated['Standard Deviation [hour]'] = np.sqrt(final_aggregated['Var_Actual_Time'])\n",
"\n",
"# Rename columns in the aggregated dataframe to match the final output\n",
"final_aggregated.rename(columns={\n",
" 'Avg_Expected_Time': 'Avg Expected Time (unit)[hour]',\n",
" 'Avg_Actual_Time': 'Avg Actual Time (unit)[hour]',\n",
" 'Var_Actual_Time': 'Variance Actual Time [hour²]',\n",
" 'Max_Expected_Time': 'Max Expected Time (unit)[hour]',\n",
"}, inplace=True)\n",
"\n",
"# Calculate 'Actual vs Standard time [%]'\n",
"final_aggregated['Actual vs Standard time [%]'] = np.where(\n",
" final_aggregated['Max Expected Time (unit)[hour]'].fillna(0) == 0,\n",
" 'N/A', # Handle division by zero\n",
" (\n",
" (((final_aggregated['Avg Actual Time (unit)[hour]'] / \n",
" final_aggregated['Max Expected Time (unit)[hour]']) - 1) * 100)\n",
" .replace([np.inf, -np.inf], np.nan) # Replace infinity values\n",
" .fillna(0) # Replace NaN values with 0\n",
" .round(0) # Round the result\n",
" .astype(int) # Convert to integer\n",
" .astype(str) + '%' # Convert to string and append '%'\n",
" )\n",
")\n",
"\n",
"# Merge the aggregated data with the original make architecture\n",
"df_Make_architure_final = pd.merge(\n",
" final_aggregated,\n",
" df_Make_architure,\n",
" on=['Pty Indice', 'IDD Component'],\n",
" how='left'\n",
").drop_duplicates()\n",
"\n",
"# Convert 'BOM Qty' and 'BOM Index' to numeric, coercing errors to NaN\n",
"df_Make_architure_final['BOM Qty'] = pd.to_numeric(df_Make_architure_final['BOM Qty'], errors='coerce')\n",
"df_Make_architure_final['BOM Index'] = pd.to_numeric(df_Make_architure_final['BOM Index'], errors='coerce')\n",
"\n",
"# Fill NaN values with appropriate placeholders\n",
"'''\n",
"Updated 10/15 to avoid FutureWarning:\n",
"1. Removed inplace=True as it will no longer work in future pandas versions.\n",
"2. Directly assign the result of fillna() back to the columns.\n",
"'''\n",
"df_Make_architure_final['BOM Qty'] = df_Make_architure_final['BOM Qty'].fillna(1)\n",
"df_Make_architure_final['BOM Index'] = df_Make_architure_final['BOM Index'].fillna(0)\n",
"\n",
"\n",
"# Convert to integer\n",
"df_Make_architure_final['BOM Qty'] = df_Make_architure_final['BOM Qty'].astype(int)\n",
"df_Make_architure_final['BOM Index'] = df_Make_architure_final['BOM Index'].astype(int)\n",
"\n",
"# Filter out rows where 'Qty_Count' is 0\n",
"df_Make_architure_final = df_Make_architure_final[df_Make_architure_final['Qty_Count'] > 0]\n",
"\n",
"# Keep only relevant columns in the final dataframe\n",
"df_Make_architure_final = df_Make_architure_final[['Priority', 'Pty Indice', 'IDD Component', 'Level', 'BOM Qty', 'Description Component', 'IDD Top Level', 'SEDA Top Level', 'M/P/F', 'Phantom', 'BOM Index', 'Avg Expected Time (unit)[hour]', 'Max Expected Time (unit)[hour]', 'Avg Actual Time (unit)[hour]', 'Variance Actual Time [hour²]', 'Standard Deviation [hour]', 'Actual vs Standard time [%]', 'WO_Count', 'Qty_Count']]\n",
"\n",
"# Drop any duplicates\n",
"df_Make_architure_final.drop_duplicates(inplace=True)\n",
"\n",
"# Round 'Avg Expected Time (unit)' and 'Avg Actual Time (unit)' to 2 decimal places\n",
"df_Make_architure_final['Avg Expected Time (unit)[hour]'] = df_Make_architure_final['Avg Expected Time (unit)[hour]'].round(2)\n",
"df_Make_architure_final['Max Expected Time (unit)[hour]'] = df_Make_architure_final['Max Expected Time (unit)[hour]'].round(2)\n",
"df_Make_architure_final['Avg Actual Time (unit)[hour]'] = df_Make_architure_final['Avg Actual Time (unit)[hour]'].round(2)\n",
"df_Make_architure_final['Variance Actual Time [hour²]'] = df_Make_architure_final['Variance Actual Time [hour²]'].round(2)\n",
"df_Make_architure_final['Standard Deviation [hour]'] = df_Make_architure_final['Standard Deviation [hour]'].round(2)\n",
"\n",
"# Groupby 'Pty Indice' and sort by 'BOM Index' for a given 'Pty Indice', then sort datafram by 'Priority' (non-numeric at the end) while keeping the previous grouping\n",
"# Define a sorting function for 'Priority' that places non-numeric values at the end\n",
"def priority_sort_key(value):\n",
" try:\n",
" # For numeric values, return a tuple where the second element is the integer value\n",
" return (0, int(value))\n",
" except ValueError:\n",
" # For non-numeric values, return a tuple where the second element is a large number (e.g., 99)\n",
" return (1, 99) # Ensuring non-numeric values are sorted at the end\n",
"\n",
"# Apply sorting within each group defined by 'Pty Indice'\n",
"df_Make_architure_final['Priority_Sort'] = df_Make_architure_final['Priority'].map(priority_sort_key)\n",
"\n",
"# Print and display the DataFrame with the Priority_Sort column\n",
"#print('df_Make_architure_final with Priority_Sort')\n",
"#display(df_Make_architure_final)\n",
"\n",
"# Sort the DataFrame by 'Priority' first\n",
"df_Make_architure_final_sorted_by_priority = df_Make_architure_final.sort_values(\n",
" by='Priority_Sort',\n",
" ascending=True\n",
")\n",
"\n",
"# Group by 'Pty Indice' and sort by 'BOM Index' within each group\n",
"df_Make_architure_final_sorted = df_Make_architure_final_sorted_by_priority.sort_values(\n",
" by=['Pty Indice', 'BOM Index'],\n",
" ascending=[True, True]\n",
")\n",
"\n",
"\n",
"# Drop the temporary 'Priority_Sort' column\n",
"df_Make_architure_final_sorted.drop(columns=['Priority_Sort'], inplace=True)\n",
"\n",
"#Create column 'Last Update' and fill the first row with the value of the first row of df_LaborReport['Last Update']\n",
"last_update_value = df_LaborReport['Last Update'].iloc[0] # Get the value from the first row\n",
"\n",
"# Create 'Last Update' column and fill it\n",
"''' Updated 10/15 to avoid warning\n",
"df_Make_architure_final_sorted['Last Update'] = np.nan # Initialize column with NaN\n",
"df_Make_architure_final_sorted.loc[0, 'Last Update'] = last_update_value # Fill the first row \n",
"'''\n",
"df_Make_architure_final_sorted['Last Update'] = np.nan # Initialize column with NaN\n",
"df_Make_architure_final_sorted['Last Update'] = df_Make_architure_final_sorted['Last Update'].astype(str) # Cast to string\n",
"df_Make_architure_final_sorted.loc[0, 'Last Update'] = last_update_value # Fill the first row with actual value\n",
"\n",
"\n",
"# Print and display the final dataframe\n",
"#print('df_Make_architure_final_sorted')\n",
"#display(df_Make_architure_final_sorted)\n",
"\n",
"# New 09/11 \n",
"###############################################################################################################\n",
"################################################ Red Formatting of |CM-LaborReport| ##########################\n",
"###############################################################################################################\n",
"# --> |CM-LaborReport| still contain all the values, while |CM-MakeArchitecture| calculation are based on a filtered datafram df_LaborReport_filtered. Apply color formating in red of the row filtered out from the datafram.\n",
"# Create a mapping of the row that have been filtered-out between df_LaborReport_filtered and df_filtered and color them in red in |CM-LaborReport| \n",
"# --> Create a boolean mask indicating which rows in df_LaborReport_filtered were kept in df_filtered\n",
"\n",
"# Define the red fill for rows that were filtered out\n",
"red_fill = PatternFill(start_color='FC8F84', end_color='FC8F84', fill_type='solid')\n",
"\n",
"# Identify the rows that were filtered out in your DataFrame\n",
"df_LaborReport_filtered['is_filtered_out'] = ~df_LaborReport_filtered['WO#'].isin(df_filtered['WO#'])\n",
"# Create a dictionary mapping WO# to is_filtered_out status\n",
"filtered_out_map = df_LaborReport_filtered.set_index('WO#')['is_filtered_out'].to_dict()\n",
"\n",
"# Iterate over rows in the existing Excel sheet\n",
"for r_idx, row in enumerate(sheet_LaborReport.iter_rows(min_row=2, max_row=sheet_LaborReport.max_row, values_only=True), start=2):\n",
" wo_number = row[5] # Assuming 'WO#' is the 6th column (index 5)\n",
" if wo_number in filtered_out_map and filtered_out_map[wo_number]:\n",
" # Apply red fill to columns F through T (indices 6 to 20)\n",
" for c_idx in range(6, 21): # 6 is F and 21 is T + 1\n",
" cell = sheet_LaborReport.cell(row=r_idx, column=c_idx)\n",
" cell.fill = red_fill\n",
" \n",
" # Apply red fill to columns V through AB (indices 22 to 28)\n",
" for c_idx in range(22, 29): # 22 is V and 29 is AB + 1\n",
" cell = sheet_LaborReport.cell(row=r_idx, column=c_idx)\n",
" cell.fill = red_fill\n",
" \n",
"'''\n",
"# Iterate over rows in the existing Excel sheet\n",
"for r_idx, row in enumerate(sheet_LaborReport.iter_rows(min_row=2, max_row=sheet_LaborReport.max_row, values_only=True), start=2):\n",
" wo_number = row[5] # Assuming 'WO#' is the 6th column (index 5)\n",
" if wo_number in filtered_out_map and filtered_out_map[wo_number]:\n",
" # Apply red fill to the entire row if it was filtered out\n",
" for c_idx in range(1, len(row) + 1):\n",
" cell = sheet_LaborReport.cell(row=r_idx, column=c_idx)\n",
" cell.fill = red_fill\n",
"'''\n",
"\n",
"#Save workbook\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"###################################################################################################################\n",
"########################################## Creating |CM-MakeArchitecture| ####################\n",
"###################################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if 'CM-MakeArchitecture' in workbook.sheetnames:\n",
" del workbook['CM-MakeArchitecture']\n",
"\n",
"# Create new \"CM-MakeArchitecture\" sheet\n",
"sheet_MakeArchi = workbook.create_sheet(title='CM-MakeArchitecture')\n",
"\n",
"# Write headers to Excel\n",
"for c_idx, header in enumerate(df_Make_architure_final_sorted.columns, start=1):\n",
" sheet_MakeArchi.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data to Excel\n",
"for r_idx, row in enumerate(df_Make_architure_final_sorted.values, start=2):\n",
" for c_idx, value in enumerate(row, start=1):\n",
" sheet_MakeArchi.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"\n",
"###############################################################################################################\n",
"################################################ Formatting |CM-MakeArchitecture| ############################\n",
"###############################################################################################################\n",
"\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_MakeArchi[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row\n",
"sheet_MakeArchi.auto_filter.ref = sheet_MakeArchi.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to S\n",
"for column in sheet_MakeArchi.iter_cols(min_col=1, max_col=19):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['A', 'B']:\n",
" sheet_MakeArchi.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['F']:\n",
" sheet_MakeArchi.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['C', 'G', 'H', 'L', 'M', 'N', 'O', 'P', 'Q']:\n",
" sheet_MakeArchi.column_dimensions[column_letter].width = 20\n",
" else:\n",
" sheet_MakeArchi.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"###############################################\n",
"# Set background color for Last Update cell T2 \n",
"###############################################\n",
"sheet_MakeArchi.cell(row=2, column=20).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"###############################################\n",
"# Coloring column 'Level' \n",
"################################################\n",
"# Custom conditional formatting for column D 'Level' (Assuming data starts from row 2)\n",
"min_row = 2\n",
"max_row = sheet_MakeArchi.max_row\n",
"col_D = 4\n",
"\n",
"for row in range(min_row, max_row + 1):\n",
" cell_value = sheet_MakeArchi.cell(row=row, column=col_D).value\n",
"\n",
" # Define default fill color in case cell_value is not in expected range\n",
" fill_color = None\n",
" \n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" \n",
" # Create PatternFill object only if fill_color is defined\n",
" if fill_color:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_MakeArchi.cell(row=row, column=col_D).fill = fill\n",
"\n",
"\n",
"################################################\n",
"# Save the updated workbook\n",
"################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Make Architecture added successfully as |CM-MakeArchi| in {original_input}\")\n",
"\n",
"\n",
"\n",
"\n",
"#****************************************************************************************************************************************************************************************\n",
"######################################################################################################################################################################################\n",
"### ###### ## ## #### #### #### #### ######### ######### ## ## ####### ### ## ######## ######## ######### ## ## ######### ############\n",
"## ## ## ## ###### ##### ###### ##### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##\n",
"### ##### ## ## ## ## ## ## ## ## ## ## ######### ######### #### + ####### ## ## ## ######## ######## ######### ######## ## ## ##\n",
"## ## ## ## ## #### ## ## #### ## ## ## ## ### ## ## ## #### ## ## ## ## ## ## ## ## ##\n",
"## ###### ######## ## ## ## ## ## ## ## ### ## ####### ## ### ## ## ## ######### ## ## ######### ##\n",
"######################################################################################################################################################################################\n",
"#****************************************************************************************************************************************************************************************\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names \n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |Summary| ...')\n",
"\n",
"# Define date and path\n",
"#input_file_formatted = original_input\n",
"supplier_file_name = os.path.join(Path, '100_item_site_settings.xlsx')\n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" #df_CTB = pd.read_excel(input_file_formatted, sheet_name='Clear-to-Build')\n",
" df_CTB = pd.read_excel(original_input, sheet_name='Clear-to-Build')\n",
" \n",
" # Load the supplier file with renamed columns\n",
" df_supplier = pd.read_excel(supplier_file_name, sheet_name=0, usecols=['Item Number', 'Name'])\n",
" df_supplier.rename(columns={'Item Number': 'IDD Component', 'Name': 'Supplier'}, inplace=True)\n",
" #print(\"Input files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"File not found: {e}\")\n",
" exit()\n",
"\n",
"########################################################\n",
"### Filtering data from input file df_CTB_formatted ###\n",
"###################################################################################################\n",
"##### Create a tab 'Summary' sumarizing the Top-Level clear-to-build and shortages on a table ######\n",
" ### 1. Create a table with the following headers from tab from tab Clear-to-Build: \n",
" ## |Pty Indice | Priority | IDD Component | Level | BOM Qty | Description | IDD Top Level | SEDA Top Level | Qty On Hand | Remain. crit. Qty | Max Qty (GS) | Max Qty Top-Level | Top Level sharing Components\n",
" ### 2. Create a new column in position 0: | Top-Level Status | to fille with 'Clear-to-build' or 'Shortage' AND second position | Supplier | \n",
" ### 3. Fill the table with the information from df_CTB:\n",
" ## Based on df_CTB, |Top-Level Status | is clear-to-build for |Max Qty Top-Level| > 0 \n",
" # fill |status| with 'Clear-to-Build' for all component of a given Top-Level only if this given component |Max Qty (GS)| < |Remain. crit. Qty|\n",
" ## Based on df_CTB, |Top-Level Status | is shortage for |Max Qty Top-Level| = 0\n",
" #fill |status| with 'shortage' for all component of a given Top-Level only if this given component |Max Qty (GS)| < |Remain. crit. Qty|\n",
" \n",
" ### 4. The colum | Supplier | should be filled based on the info from df_supplier \n",
" ### 5. Create a new row for the Top-Level with |Level| = 0 on top of each subset/list of component related to a given Top-Level to facilitate the reading \n",
"\n",
"##############################################################################################################################\n",
"# Filtering data from input file df_CTB_formatted\n",
"##############################################################################################################################\n",
"# Define the columns to be filtered from df_CTB\n",
"filtered_columns = ['Pty Indice', 'Priority', 'IDD Component', 'Level', 'BOM Qty', 'Description',\n",
" 'IDD Top Level', 'SEDA Top Level', 'Qty On Hand', 'Remain. crit. Qty', 'Max Qty (GS)',\n",
" 'Max Qty Top-Level', 'Top Level sharing Components', 'Pur/Mfg']\n",
"\n",
"# Filter relevant columns and create a copy\n",
"df_summary = df_CTB[filtered_columns].copy()\n",
"\n",
"# Convert 'Max Qty Top-Level' column to numeric, coercing errors to NaN\n",
"df_summary['Max Qty Top-Level'] = pd.to_numeric(df_summary['Max Qty Top-Level'], errors='coerce')\n",
"\n",
"# Create 'Top-Level Status' column based on comparison\n",
"df_summary['Top-Level Status'] = np.where(df_summary['Max Qty Top-Level'] > 0, 'Clear-to-Build', 'Shortage')\n",
"\n",
"# Merge information from df_supplier\n",
"df_summary = df_summary.merge(df_supplier, how='left', on='IDD Component')\n",
"\n",
"# Fill NaN values in the 'Supplier' column with 'Supplier TBD' initially\n",
"# df_summary['Supplier'].fillna('Supplier TBD', inplace=True) - Replaced 09/24 to avoid warning\n",
"df_summary['Supplier'] = df_summary['Supplier'].fillna('Supplier TBD')\n",
"\n",
"# Update 'Supplier' to 'Make Part' if 'Pur/Mfg' is 'M' and 'Supplier' is 'Supplier TBD'\n",
"#df_summary.loc[(df_summary['Pur/Mfg'] == 'M') & (df_summary['Supplier'] == 'Supplier TBD'), 'Supplier'] = 'Make Part'\n",
"\n",
"# Update 'Supplier' to 'Make Part' if 'Pur/Mfg' is 'M' \n",
"df_summary.loc[df_summary['Pur/Mfg'] == 'M', 'Supplier'] = 'Make Part'\n",
"\n",
"###################################################################################\n",
"######### New 09/20 ##########\n",
"###################################################################################\n",
"# Update Supplier to 'Make Part CUU' if 'Pur/Mfg' is 'D' \n",
"df_summary.loc[df_summary['Pur/Mfg'] == 'D', 'Supplier'] = 'Make Part CUU'\n",
"\n",
"# Convert 'BOM Qty' to numeric, coercing errors to NaN\n",
"df_summary['BOM Qty'] = pd.to_numeric(df_summary['BOM Qty'], errors='coerce')\n",
"\n",
"# Filter out rows where 'BOM Qty' is a float (keep NaN and integers intact)\n",
"df_summary = df_summary[df_summary['BOM Qty'].apply(lambda x: x.is_integer() if pd.notna(x) else True)]\n",
"###################################################################################\n",
"# Updated 09/23 to not considered rows where Pur/Mfg' == 'D' to define 'Max Qty Top-Level' --> Write 'Make Part CUU' on 'Max Qty (GS)' so it won't be considered in df_Summary\n",
"###################################################################################\n",
"# Drop the 'Pur/Mfg' column as it is no longer needed\n",
"df_summary.drop(columns=['Pur/Mfg'], inplace=True)\n",
"\n",
"# Filter out rows where 'Remain. crit. Qty' is 'Completed'\n",
"df_summary_filtered = df_summary[df_summary['Remain. crit. Qty'] != 'Completed']\n",
"\n",
"# Convert 'Max Qty (GS)' column to numeric, coercing errors to NaN\n",
"df_summary_filtered.loc[:, 'Max Qty (GS)'] = pd.to_numeric(df_summary_filtered['Max Qty (GS)'], errors='coerce')\n",
"\n",
"# Filter the DataFrame to keep only rows where 'Max Qty Top-Level' is not empty\n",
"df_summary_filtered = df_summary_filtered[df_summary_filtered['Max Qty Top-Level'].notnull()]\n",
"\n",
"# Convert 'Remain. crit. Qty' and 'Max Qty (GS)' columns to numeric, coercing errors to NaN for non-numeric values\n",
"df_summary_filtered['Remain. crit. Qty'] = pd.to_numeric(df_summary_filtered['Remain. crit. Qty'], errors='coerce')\n",
"df_summary_filtered['Max Qty (GS)'] = pd.to_numeric(df_summary_filtered['Max Qty (GS)'], errors='coerce')\n",
"\n",
"# Create a mask to filter out non-numeric values - Updated on 08/09 to include PN where Remain. crit. Qty is NaN if 'Max Qty (GS)' is >= 0\n",
"#numeric_mask = ~df_summary_filtered['Remain. crit. Qty'].isna() & ~df_summary_filtered['Max Qty (GS)'].isna()\n",
"\n",
"# New 08/09 - Create the mask including PN where Remain. crit. Qty is NaN if 'Max Qty (GS)' is >= 0 --> Follow-up order for a PN with an existing BOM \n",
"numeric_mask = (\n",
" ((df_summary_filtered['Max Qty (GS)'] >= 0)) |\n",
" (~df_summary_filtered['Remain. crit. Qty'].isna() & ~df_summary_filtered['Max Qty (GS)'].isna())\n",
")\n",
"\n",
"\n",
"# Filter the DataFrame to keep only rows where both 'Max Qty (GS)' and 'Remain. crit. Qty' are numeric\n",
"df_summary_filtered = df_summary_filtered[numeric_mask]\n",
"\n",
"#################################################################\n",
"# PN with Backlog - Hidden on 08/09 to include PN with Backlog \n",
"####################################################################\n",
"'''\n",
"# Filter the DataFrame to keep only rows where 'Max Qty (GS)' is lower than 'Remain. crit. Qty' - Update 07/30 to include PN where 'Remain. crit. Qty' = 0 but IDD Backlog exists (from df_backlog)\n",
"df_summary_filtered = df_summary_filtered[df_summary_filtered['Max Qty (GS)'] < df_summary_filtered['Remain. crit. Qty']]\n",
"\n",
"#New code 07/30\n",
"#Filter df_summary_filtered based on 'Max Qty (GS)' < 'Remain. crit. Qty'\n",
"df_summary_filtered = df_summary_filtered[df_summary_filtered['Max Qty (GS)'] < df_summary_filtered['Remain. crit. Qty']]\n",
"\n",
"# Identify 'Pty Indice' in df_backlog with 'Order' starting with 'S' or 'D' (filter out NC) -- Only PN for wich IDD has a backlog\n",
"valid_indices = df_backlog[df_backlog['Order'].str.startswith(('S', 'D'))]['Pty Indice'].unique() -- Update 08/08 to include PN NOT in IDD backlog \n",
"\n",
"# Include rows in df_summary_filtered where 'Remain. crit. Qty' is not 0, or 'Pty Indice' is in valid_indices -- CHanged 08/08 'Remain. crit. Qty' can be 0\n",
"df_summary_filtered_with_backlog = df_summary_filtered[\n",
" (df_summary_filtered['Remain. crit. Qty'] != 0) |\n",
" (df_summary_filtered['Pty Indice'].isin(valid_indices))\n",
"]\n",
"\n",
"\n",
"# Include rows in df_summary_filtered where 'Remain. crit. Qty' is not 0, or 'Pty Indice' is in valid_indices -- Changed 08/08 'Remain. crit. Qty' can be 0 so we can see the follow-up order\n",
"#df_summary_filtered_with_backlog = df_summary_filtered[(df_summary_filtered['Pty Indice'].isin(valid_indices))]\n",
"\n",
"# Copy df_summary_filtered_with_backlog as df_summary_filtered\n",
"df_summary_filtered = df_summary_filtered_with_backlog\n",
"'''\n",
"######################################################################\n",
"\n",
"# Reorder columns with \"Top-Level Status\" and \"Supplier\" as the first two columns\n",
"df_summary = df_summary_filtered[['Top-Level Status', 'Supplier'] + [col for col in df_summary_filtered.columns if col not in ['Top-Level Status', 'Supplier']]]\n",
"\n",
"#print('df_summary:')\n",
"# Display or further process df_summary\n",
"#display(df_summary)\n",
"\n",
"##############################################################################################################################\n",
"# Include fully clear to build Top-Level & Display a row for every given Top-Level\n",
"##############################################################################################################################\n",
"# Get unique top-level components\n",
"top_levels = df_summary['IDD Top Level'].unique()\n",
"\n",
"# Create rows for top-level components\n",
"top_level_rows = []\n",
"for top_level in top_levels:\n",
" subcomponents = df_summary[df_summary['IDD Top Level'] == top_level]\n",
" remain_crit_qty = subcomponents['Remain. crit. Qty'].iloc[0]\n",
" max_qty_top_level = subcomponents['Max Qty Top-Level'].iloc[0]\n",
" # Create a row with the necessary columns\n",
" row = {\n",
" 'Top-Level Status': top_level,\n",
" 'Supplier': '',\n",
" 'Pty Indice': subcomponents['Pty Indice'].iloc[0],\n",
" 'Priority': subcomponents['Priority'].iloc[0],\n",
" 'IDD Component': top_level,\n",
" 'Level': 0,\n",
" 'BOM Qty': np.nan,\n",
" 'Description': '',\n",
" 'IDD Top Level': top_level,\n",
" 'SEDA Top Level': '',\n",
" 'Qty On Hand': np.nan,\n",
" 'Remain. crit. Qty': remain_crit_qty,\n",
" 'Max Qty (GS)': np.nan,\n",
" 'Max Qty Top-Level': max_qty_top_level,\n",
" 'Top Level sharing Components': ''\n",
" }\n",
" top_level_rows.append(row)\n",
"\n",
"# Convert the list of dictionaries to a DataFrame\n",
"df_top_levels = pd.DataFrame(top_level_rows)\n",
"\n",
"# Append the top-level rows to the filtered DataFrame\n",
"df_summary_final = pd.concat([df_top_levels, df_summary], ignore_index=True)\n",
"\n",
"#print('df_summary_final:')\n",
"#display(df_top_levels) \n",
"\n",
"#########################################################\n",
"# Include Top-Level fully clear to build based on df_CTB\n",
"########################################################\n",
"# Convert 'Max Qty Top-Level' and 'Remain. crit. Qty' to numeric, coercing errors to NaN\n",
"df_CTB['Max Qty Top-Level'] = pd.to_numeric(df_CTB['Max Qty Top-Level'], errors='coerce')\n",
"df_CTB['Remain. crit. Qty'] = pd.to_numeric(df_CTB['Remain. crit. Qty'], errors='coerce')\n",
"\n",
"# Filter the DataFrame to include only rows where 'Max Qty Top-Level' is greater than 'Remain. crit. Qty' - Update 08/08, I want to see pty indice with follow-up order \n",
"#df_clear_to_build = df_CTB[df_CTB['Max Qty Top-Level'] > df_CTB['Remain. crit. Qty']]\n",
"#Updated 08/08: \n",
"# Filter the DataFrame where 'Max Qty Top-Level' is not NaN and is >= 0 --> Should include ALL PN (clear and short) from |Clear-to-Build| meanning PN with an existing BOM in |Summary| \n",
"df_clear_to_build = df_CTB[pd.notna(df_CTB['Max Qty Top-Level']) & (df_CTB['Max Qty Top-Level'] >= 0)]\n",
"\n",
"# Drop duplicates to ensure only one row per top-level component\n",
"df_clear_to_build = df_clear_to_build.drop_duplicates(subset=['IDD Top Level'])\n",
"\n",
"# Create a new DataFrame for the top-level components based on the components\n",
"top_level_columns = ['Pty Indice', 'Priority', 'IDD Component', 'IDD Top Level', 'Remain. crit. Qty', 'Max Qty Top-Level']\n",
"\n",
"# Initialize df_top_level with columns from df_clear_to_build\n",
"df_top_level = df_clear_to_build[top_level_columns].copy()\n",
"\n",
"# Set the 'Level' column to 0 only where IDD Component matches IDD Top Level\n",
"df_top_level.loc[df_top_level['IDD Component'] == df_top_level['IDD Top Level'], 'Level'] = 0 \n",
"\n",
"# Set the 'Top-Level Status' column to the value of 'IDD Top Level'\n",
"df_top_level['Top-Level Status'] = df_top_level['IDD Top Level']\n",
"\n",
"# Filter out rows where 'Max Qty (GS)' is empty and 'Level' is not 0\n",
"df_summary_final = df_summary_final[~((df_summary_final['Max Qty (GS)'].isna()) & (df_summary_final['Level'] != 0))]\n",
"\n",
"# Concatenate the top-level rows with the existing summary DataFrame\n",
"df_summary_with_top_level = pd.concat([df_summary_final, df_top_level], ignore_index=True)\n",
"\n",
"# Ensure no row has Max Qty (GS) empty if Level is not 0 in the final DataFrame\n",
"df_summary_with_top_level = df_summary_with_top_level[~((df_summary_with_top_level['Max Qty (GS)'].isna()) & (df_summary_with_top_level['Level'] != 0))]\n",
"\n",
"#Delete potentiel duplicate component for a given Pty Indice - Some component appears several time on the BOM and on the |clear-to-build| tab\n",
"df_summary_with_top_level = df_summary_with_top_level.drop_duplicates(subset=['Pty Indice', 'IDD Component'])\n",
"\n",
"###################################################################################\n",
"### Sorting dataframe by 'Pty Indice' and then by 'Level' within 'Pty Indice' ###\n",
"##################################################################################\n",
"# df_summary_sorted = df_summary_with_top_level.groupby('Pty Indice', group_keys=False).apply(lambda x: x.sort_values(by=['Level']).sort_values(by='Priority')) # Replaced 09/24 to avoid warning\n",
"\n",
"#########################################################\n",
"################### Update 09/24 ###################\n",
"# Apply the sorting while keeping the grouping columns\n",
"df_summary_sorted = (df_summary_with_top_level\n",
" .groupby('Pty Indice', group_keys=False)\n",
" .apply(lambda x: x.sort_values(by='Level').sort_values(by='Priority'))\n",
" )\n",
"\n",
"# Resetting the index to keep the grouping columns and remove any warnings\n",
"df_summary_sorted = df_summary_sorted.reset_index(drop=True)\n",
"############################\n",
"\n",
"\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating SUMMARY #########################\n",
"###################################################################################################################\n",
"# Check if \"Summary\" sheet already exists\n",
"if \"Summary\" in workbook.sheetnames:\n",
" # Remove the existing \"Summary\" sheet\n",
" workbook.remove(workbook[\"Summary\"])\n",
"\n",
"# Create a new \"Summary\" sheet\n",
"Summary_sheet = workbook.create_sheet(title='Summary', index=0) # Add as the 1st sheet (index 0)\n",
"\n",
"# Write headers\n",
"for c_idx, header in enumerate(df_summary_sorted.columns, start=1):\n",
" Summary_sheet.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data\n",
"for r_idx, row in enumerate(df_summary_sorted.values, start=2): # Start from row 2\n",
" for c_idx, value in enumerate(row, start=1):\n",
" Summary_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"# Save the updated workbook\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Summary added successfully as |Summary| in {original_input}\")\n",
"\n",
"################################################################################################################\n",
"#***************************************************************************************************************\n",
"# Creating tab 'Snapshot' in first position \n",
"#***************************************************************************************************************\n",
"################################################################################################################\n",
"##### load the formatted workbook & print sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |Snapshot| ...')\n",
"\n",
"df_summary = pd.read_excel(original_input, sheet_name='Summary')\n",
"df_priority = pd.read_excel(original_input, sheet_name='CM-Priority')\n",
"df_backlog = pd.read_excel(original_input, sheet_name='CM-Backlog')\n",
"df_CTB = pd.read_excel(original_input, sheet_name='Clear-to-Build')\n",
"df_WIP = pd.read_excel(original_input, sheet_name='CM-WIP')\n",
"\n",
"########################################################\n",
"### Filtering data from input tab SUMMARY & CM-Priority\n",
"########################################################\n",
"# Create a summary table with required columns\n",
"df_snapshot = df_summary[['Top-Level Status', 'Pty Indice', 'Priority', 'IDD Top Level', 'SEDA Top Level', 'Remain. crit. Qty', 'Max Qty Top-Level']]\n",
"\n",
"''' To be deleted \n",
"# Merge with 'CM-Priority' to get 'Production Status' and 'Description'\n",
"df_snapshot = pd.merge(df_snapshot, df_priority[['Pty Indice', 'Description', 'Shipped','Production Status', 'Start date target', 'IDD Sale Price', 'SEDA Sale Price']], on='Pty Indice', how='inner')\n",
"\n",
"# Exclude rows where 'Production Status' is 'To be transferred' or 'Not phase 4'\n",
"df_snapshot = df_snapshot[~df_snapshot['Production Status'].isin(['To be transferred', 'Not phase 4','Canceled'])]\n",
"''' \n",
"\n",
"# Ensure 'Pty Indice' in df_priority is unique\n",
"df_priority_unique = df_priority.drop_duplicates(subset='Pty Indice', keep='first')\n",
"\n",
"# Merge df_snapshot with df_priority to get all necessary columns\n",
"df_snapshot = pd.merge(df_snapshot, df_priority_unique[['Pty Indice', 'Description', 'Shipped', 'Production Status', 'Start date target', 'IDD Sale Price', 'SEDA Sale Price']], on='Pty Indice', how='left')\n",
"\n",
"# Exclude rows where 'Production Status' is 'To be transferred', 'Not phase 4', or 'Canceled' - update 08/09, line not needed as this part are now included on df_Summary\n",
"#df_snapshot = df_snapshot[~df_snapshot['Production Status'].isin(['To be transferred', 'Not phase 4', 'Canceled'])]\n",
"\n",
"###############################################################################################\n",
"# Include fully clear-to-build PN\n",
"# Mostly necesseary for lightplate as very fiew P Part and most of the work is at a Top-Level \n",
"#############################################################################################\n",
"# --->>> #New code 07/24/24 <--\n",
"# Convert columns to numeric, coercing errors to NaN\n",
"df_CTB['Max Qty (GS)'] = pd.to_numeric(df_CTB['Max Qty (GS)'], errors='coerce')\n",
"df_CTB['Remain. crit. Qty'] = pd.to_numeric(df_CTB['Remain. crit. Qty'], errors='coerce')\n",
"\n",
"# Filter df_CTB to remove rows with NaN in 'Max Qty (GS)' and apply custom filter\n",
"filtered_ctb = df_CTB.dropna(subset=['Max Qty (GS)'])\n",
"filtered_ctb = filtered_ctb[filtered_ctb['Max Qty (GS)'] > filtered_ctb['Remain. crit. Qty']]\n",
"\n",
"# Group by 'Pty Indice'\n",
"grouped = filtered_ctb.groupby('Pty Indice')\n",
"\n",
"# Find the row with the lowest 'Max Qty (GS)' in each group\n",
"#min_qty_rows = grouped.apply(lambda df: df.loc[df['Max Qty (GS)'].idxmin()]) - Replaced 09/24 to avoid warning\n",
"min_qty_rows = grouped.apply(lambda df: df.loc[df['Max Qty (GS)'].idxmin()]).reset_index(drop=True)\n",
"\n",
"# Reset the index because the groupby + apply operation sets a multi-index\n",
"#min_qty_rows = min_qty_rows.reset_index(drop=True) # No need to call reset_index again as it's already reset in the previous line\n",
"\n",
"# Concatenate these rows with df_snapshot\n",
"df_snapshot = pd.concat([df_snapshot, min_qty_rows], ignore_index=True)\n",
"\n",
"# Print the result\n",
"#print(\"Column names in df_snapshot:\")\n",
"#print(df_snapshot.columns)\n",
"#print(\"Updated df_snapshot:\")\n",
"#display(df_snapshot)\n",
"\n",
"\n",
"# Some part are not present on the backlog and won't appear on df_snapshot (P14B for example) --> Need to include the part that are not in the backlog but present in df_Historic and set backlog qty to 0\n",
"#############################################################################################\n",
"# Ensure 'Pty Indice' in df_priority is unique\n",
"df_priority_unique = df_priority.drop_duplicates(subset='Pty Indice', keep='first')\n",
"df_priority_unique.set_index('Pty Indice', inplace=True)\n",
"\n",
"# Fill 'Production Status' for new rows based on 'CM-Priority'\n",
"df_snapshot['Production Status'] = df_snapshot['Pty Indice'].map(df_priority_unique['Production Status'])\n",
"\n",
"# Update 'Description' similarly\n",
"df_snapshot['Description'] = df_snapshot['Pty Indice'].map(df_priority_unique['Description'])\n",
"\n",
"# Rename 'Max Qty Top-Level' to 'Qty clear to build'\n",
"df_snapshot.rename(columns={'Max Qty Top-Level': 'Qty clear to build'}, inplace=True)\n",
"\n",
"#################################################################################################################\n",
"# Create new rows in df_snapshot for missing PN (not in |Summary| because no Purchase Part) -- P14B for example \n",
"#################################################################################################################\n",
"# New code 07/29 \n",
"######################\n",
"### Include only missing PN related to Phase 4 as Summary is only showing Phase 4 at this point \n",
"# Create new rows for missing 'Pty Indice' in Phase 4\n",
"missing_pn_phase4 = df_priority[(df_priority['Program'] == 'Phase 4') & (~df_priority['Pty Indice'].isin(df_snapshot['Pty Indice']))]\n",
"\n",
"# Prepare the new rows with 'Clear-to-Build' status\n",
"new_rows = missing_pn_phase4[['Pty Indice', 'Priority', 'IDD Top Level', 'SEDA Top Level', 'Shipped', 'Remain. crit. Qty', 'Description', 'Production Status']]\n",
"\n",
"#print('new_rows')\n",
"#display(new_rows)\n",
"\n",
"################\n",
"#New code 07/30 - Update 08/09, carefull to always have 'Remain. crit. Qty' filled on |clear-to-build| even though they are from the redlist 'To be transferred' or 'Canceled' \n",
"# otherwise these PN will be filter-out and introduced later and consiedered as PN without any purchases parts (like some lightplate) and the 'Qty clear to build' will be set to the maximmun demand even though they might be short\n",
"#######################################################################################\n",
"# Filter out rows where 'Production Status' is in ['To be transferred', 'Canceled']\n",
"# AND 'Remain. crit. Qty' is either empty OR 'Qty clear to build' = 0\n",
"#########################################################################################\n",
"filtered_new_rows = new_rows[\n",
" ~(\n",
" (new_rows['Production Status'].isin(['To be transferred', 'Canceled'])) &\n",
" (new_rows['Remain. crit. Qty'].isna() | (new_rows['Remain. crit. Qty'] == ''))\n",
" )\n",
"]\n",
"\n",
"#print('filtered_new_rows')\n",
"#display (filtered_new_rows) -- filtered_new_rows all the missing PN that should be included on Snapshot\n",
"\n",
"filtered_new_rows = filtered_new_rows.copy()\n",
"\n",
"# Merge new rows with the existing snapshot \n",
"df_snapshot = pd.concat([df_snapshot, filtered_new_rows], ignore_index=True)\n",
"\n",
"#####################################################################################################\n",
"#********************************************************************************\n",
"# Update |Snapshot| based on |CM-Backlog| - Include after creation of Backlog\n",
"#********************************************************************************\n",
"###Row from |CM-Backlog| with 'Inv. Addr' = 10178-1 ('Invoice name' = 'IDD Aerospace Corporation') should be excluded, this is Labortest WO.\n",
"# --> Filter out the specific row from CM-Backlog that are LABORTEST WO\n",
"df_backlog = df_backlog[df_backlog['Inv. Addr'] != '10178-1']\n",
"\n",
"##############################################################################################\n",
"# Create column |IDD Backlog Qty| to determine |Remaining Crit. Qty| vs. |IDD Backlog Qty|\n",
"#|IDD Backlog Qty| = |Backlog row Qty| for each row for a given |IDD Top Leve| exept if |Order| contain 'NC'\n",
"#############################################################################################\n",
"# Load backlog data and calculate IDD Backlog Qty\n",
"df_backlog['IDD Backlog Qty'] = df_backlog.apply(lambda row: row['Backlog row Qty'] if 'NC' not in row['Order'] else 0, axis=1)\n",
"\n",
"# Aggregate backlog quantities by 'IDD Top Level'\n",
"df_backlog_agg = df_backlog.groupby('IDD Top Level')['IDD Backlog Qty'].sum().reset_index()\n",
"\n",
"# Merge backlog quantities with snapshot\n",
"df_snapshot = pd.merge(df_snapshot, df_backlog_agg, on='IDD Top Level', how='left')\n",
"\n",
"# Replace NaN values in 'IDD Backlog Qty' with 0\n",
"df_snapshot['IDD Backlog Qty'] = df_snapshot['IDD Backlog Qty'].fillna(0)\n",
"\n",
"# New 08/08 - Filter-out \n",
"# Filter out rows where 'IDD Backlog Qty' is zero\n",
"#df_snapshot = df_snapshot[df_snapshot['IDD Backlog Qty'] != 0]\n",
"\n",
"#####################################################\n",
"# NEW 09/16 --> do not filter-out with completed PN\n",
"######################################################\n",
"# Filter out rows where 'IDD Backlog Qty' is zero and 'Production Status' is 'Completed'\n",
"# df_snapshot = df_snapshot[~((df_snapshot['IDD Backlog Qty'] == 0) & (df_snapshot['Production Status'].isin(['Completed'])))] <-- Erased 09/16\n",
"\n",
"#filter-out 'Pty Indice' that are not on the df_CM_BOM as without BOM it should not be included\n",
"df_snapshot = df_snapshot[df_snapshot['Pty Indice'].isin(df_CM_BOM['Pty Indice'])]\n",
"\n",
"# Optionally, reset the index after filtering\n",
"df_snapshot.reset_index(drop=True, inplace=True)\n",
"\n",
"##############################################################################\n",
"# Update 'Qty clear to build' based on 'IDD Backlog Qty' - Max of 'Qty clear to build' should be the Max of ('IDD Backlog Qty' and 'Remain. crit. Qty')\n",
"############################################################################\n",
"#New code 07/25\n",
"# Ensure values are numerical\n",
"df_snapshot['Remain. crit. Qty'] = pd.to_numeric(df_snapshot['Remain. crit. Qty'], errors='coerce')\n",
"df_snapshot['IDD Backlog Qty'] = pd.to_numeric(df_snapshot['IDD Backlog Qty'], errors='coerce')\n",
"\n",
"# Ensure 'Qty clear to build' is updated based on 'IDD Backlog Qty' and 'Remain. crit. Qty' only if it is initially greater than both - To avoid having to big of a number for lightplates\n",
"df_snapshot['Qty clear to build'] = df_snapshot.apply(\n",
" lambda row: max(row['Remain. crit. Qty'], row['IDD Backlog Qty'])\n",
" if row['Qty clear to build'] > row['Remain. crit. Qty'] and row['Qty clear to build'] > row['IDD Backlog Qty']\n",
" else row['Qty clear to build'],\n",
" axis=1\n",
")\n",
"\n",
"# New code 07/30 - 08/08 --> Carefull that ALL PN related to Phase4 from CM-Priority have a 'Critical qty' set to some number to avoid filling these PN with an completly innacurate 'Qty CTB' \n",
"# At this point, only the PN without any purchased part on the BOM will have Qty clear to build' empty (mainly lightplates) \n",
"#If 'Qty clear to build' is empty, fill 'Qty clear to build' with the biggest value between 'Remain. crit. Qty' and 'IDD Backlog Qty'\n",
"df_snapshot['Qty clear to build'] = df_snapshot['Qty clear to build'].fillna(\n",
" df_snapshot[['Remain. crit. Qty', 'IDD Backlog Qty']].max(axis=1)\n",
")\n",
"\n",
"#Update 07/30 --- change position of code bellow\n",
"# Set 'Top-Level Status' based on the 'Qty clear to build'\n",
"df_snapshot['Top-Level Status'] = df_snapshot.apply(lambda row: 'Clear-to-Build' if row['Qty clear to build'] > 0 else 'Short', axis=1)\n",
"\n",
"# Drop duplicates of 'Pty Indice' and keep the row where 'SEDA Top Level' is not empty\n",
"# If 'SEDA Top Level' is empty for all duplicates, keep the first occurrence\n",
"df_snapshot = df_snapshot.sort_values(by=['Pty Indice', 'SEDA Top Level'], ascending=[True, False])\n",
"df_snapshot = df_snapshot.drop_duplicates(subset='Pty Indice', keep='first')\n",
"\n",
"# Keep only the necessary columns\n",
"df_snapshot = df_snapshot[['Top-Level Status', 'Pty Indice', 'Priority', 'IDD Top Level', 'SEDA Top Level', 'Shipped', 'Remain. crit. Qty', 'Qty clear to build', 'Description', 'Production Status', 'Start date target', 'IDD Backlog Qty']]\n",
"\n",
"# Convert and format 'Start date target' in one step\n",
"df_snapshot['Start date target'] = pd.to_datetime(df_snapshot['Start date target'], format='%m/%d/%Y', errors='coerce').dt.strftime('%m/%d/%Y')\n",
"\n",
"############################################\n",
"# Function to determine the product category\n",
"############################################\n",
"# Define the 'Product Category' based on 'General Description'\n",
"def determine_category(description):\n",
" if not isinstance(description, str):\n",
" return 'Others'\n",
" if description == 'Rototellite':\n",
" return 'Rototellite'\n",
" elif 'Indicator' in description or 'CPA' in description:\n",
" return 'CPA'\n",
" elif 'Lightplate' in description:\n",
" return 'Lightplate'\n",
" elif 'ISP' in description or 'Keyboard' in description:\n",
" return 'ISP'\n",
" elif 'Module' in description:\n",
" return 'CPA'\n",
" elif 'optics' in description:\n",
" return 'Fiber Optics'\n",
" else:\n",
" return 'Others'\n",
"\n",
"# Create 'Product Category' column based on the 'Description'\n",
"df_snapshot['Product Category'] = df_snapshot['Description'].apply(determine_category)\n",
"\n",
"########################################################################################\n",
"# Determine 'IDD Marge Standard (unit)' & 'IDD Sale Price' from CM-Backlog\n",
"#######################################################################################\n",
"''' SAVED 10/07 to be updated to avoid getting $0 as 'IDD Sale Price'\n",
"# Group by 'IDD Top Level' and calculate required values from df_backlog\n",
"df_backlog_grouped = df_backlog.groupby('IDD Top Level').agg({\n",
" 'Marge standard': 'first', \n",
" 'Backlog row Qty': 'first',\n",
" 'Currency net amount': 'first' \n",
"}).reset_index()\n",
"\n",
"df_backlog_grouped['IDD Marge Standard (unit)'] = (df_backlog_grouped['Marge standard'] / df_backlog_grouped['Backlog row Qty']).round(1)\n",
"df_backlog_grouped['IDD Sale Price'] = (df_backlog_grouped['Currency net amount'] / df_backlog_grouped['Backlog row Qty']).round(1)\n",
"\n",
"# Merge these calculated columns back to the df_snapshot\n",
"df_snapshot = pd.merge(df_snapshot, df_backlog_grouped[['IDD Top Level', 'IDD Marge Standard (unit)', 'IDD Sale Price']], on='IDD Top Level', how='left')\n",
"'''\n",
"\n",
"#Update 10/07 to filter-out 'Order' containg NC --> To avoid getting $0 as 'IDD Sale Price' because of NC at the beggining of the Backlog.\n",
"# Step 1: Filter out rows where 'Order' contains 'NC'\n",
"df_backlog_filtered_without_NC = df_backlog[~df_backlog['Order'].str.contains('NC', na=False)]\n",
"\n",
"# Step 2: Determine 'IDD Marge Standard (unit)' & 'IDD Sale Price' from CM-Backlog\n",
"# Group by 'IDD Top Level' and calculate required values from the filtered df_backlog\n",
"df_backlog_grouped = df_backlog_filtered_without_NC.groupby('IDD Top Level').agg({\n",
" 'Marge standard': 'first', \n",
" 'Backlog row Qty': 'first',\n",
" 'Currency net amount': 'first' \n",
"}).reset_index()\n",
"\n",
"df_backlog_grouped['IDD Marge Standard (unit)'] = (df_backlog_grouped['Marge standard'] / df_backlog_grouped['Backlog row Qty']).round(1)\n",
"df_backlog_grouped['IDD Sale Price'] = (df_backlog_grouped['Currency net amount'] / df_backlog_grouped['Backlog row Qty']).round(1)\n",
"\n",
"# Merge these calculated columns back to the df_snapshot\n",
"df_snapshot = pd.merge(df_snapshot, df_backlog_grouped[['IDD Top Level', 'IDD Marge Standard (unit)', 'IDD Sale Price']], on='IDD Top Level', how='left')\n",
"\n",
"#--> \n",
"################################\n",
"# WIP 09/17 \n",
"###################################################################################################################################################################\n",
"### Update Snapshot (only rows for the parts that are completed without follow-up order for analysis purposes) by calculating Financial KPI based on df_Historic & set the backlog/production KPI to 0 [P2's, P14B ...]\n",
"## Update KPI metrics based on df_Historic for the PN present on df_snapshot but not in the backlog for wich 'IDD Current Margin (%)' = 'N/A' due to missing financial metrics from the backlog (only possible if present on df_Historic) [P14A]\n",
"###################################################################################################################################################################\n",
"### Apply this to the row in df_snapshot with 'Pty Indice' not present in df_backlog but present in df_Historic \n",
"### Set 'Top-Level Status' to 'Completed - No Backlog' as the current status should be 'Shortage' which make sense as there is not backlog --> Deferiencation for this specific rows \n",
"## Calculation of the KPI metrics for a given 'Pty Indice': \n",
"# --> Keep last row on df_Historic and calculate df_snapshot['IDD Marge Standard (unit)'] = (df_Historic['Currency turnover ex.VAT'] - df_Historic['Standard amount USD')]/df_Historic['Quantity'], \n",
"# --> Keep last row on df_Historic and set df_snapshot['IDD Sale Price'] = df_Historic['Currency turnover ex.VAT']/df_Historic['Quantity']\n",
"# df_snapshot['IDD Current Margin (%)'] is calculated later on the code\n",
"# df_snapshot['IDD Expected ROI (Total)'] is calculated later on the code\n",
"\n",
"# Create a boolean mask for each 'Pty Indice' indicating whether all 'Order' values from the backlog contain 'NC'\n",
"backlog_nc_only_mask = df_backlog.groupby('Pty Indice')['Order'].apply(lambda x: (x.str.contains('NC', na=False).all()))\n",
"\n",
"# Filter 'Pty Indice' that have only 'NC' in their 'Order'\n",
"backlog_nc_only = backlog_nc_only_mask[backlog_nc_only_mask].index\n",
"\n",
"# Convert the index to a list of unique 'Pty Indice'\n",
"backlog_nc_only = backlog_nc_only.tolist()\n",
"\n",
"# Identify `Pty Indice` in df_snapshot that are not in df_backlog but are present in df_Historic\n",
"df_snapshot_missing = df_snapshot[\n",
" (~df_snapshot['Pty Indice'].isin(df_backlog['Pty Indice']) | df_snapshot['Pty Indice'].isin(backlog_nc_only)) &\n",
" df_snapshot['Pty Indice'].isin(df_Historic['Pty Indice'])\n",
"]\n",
"\n",
"print(\"Pty Indice to update:\", df_snapshot_missing['Pty Indice'].unique())\n",
"\n",
"#############################################################################\n",
"# Update 09/20 - Ensure that only a few rows are updated with df_Historic \n",
"#############################################################################\n",
"# Check if there are rows to update\n",
"if df_snapshot_missing.empty:\n",
" print(\"No rows to update in df_snapshot_missing.\")\n",
"else:\n",
" # Count the number of rows to be updated\n",
" num_rows_to_update = len(df_snapshot_missing)\n",
"\n",
" # Exclude rows in df_Historic where 'Order' contains 'NC'\n",
" df_Historic_filtered = df_Historic[~df_Historic['Order'].str.contains('NC', na=False)]\n",
"\n",
" # Fetch the last row from the filtered df_Historic for each 'Pty Indice' present in df_snapshot_missing - Nope, the most recent not the last\n",
" #df_Historic_last = df_Historic_filtered[df_Historic_filtered['Pty Indice'].isin(df_snapshot_missing['Pty Indice'])].groupby('Pty Indice').last().reset_index()\n",
"\n",
" # Convert 'Invoice date' in df_Historic to datetime\n",
" df_Historic['Invoice date'] = pd.to_datetime(df_Historic['Invoice date'], errors='coerce') # Handle any invalid dates\n",
"\n",
" # Sort df_Historic_filtered by 'Pty Indice' and 'Invoice date' in descending order to get the most recent row for each 'Pty Indice'\n",
" df_Historic_filtered = df_Historic_filtered.sort_values(by=['Pty Indice', 'Invoice date'], ascending=[True, False])\n",
"\n",
" # Fetch the most recent row for each 'Pty Indice' based on 'Invoice date'\n",
" df_Historic_mostrecent = df_Historic_filtered.groupby('Pty Indice').first().reset_index()\n",
"\n",
" # Merge df_snapshot_missing with df_Historic_mostrecent based on 'Pty Indice' to get corresponding financial metrics\n",
" df_snapshot_missing_updated = df_snapshot_missing.merge(\n",
" df_Historic_mostrecent[['Pty Indice', 'Currency turnover ex.VAT', 'Standard amount USD', 'Quantity']],\n",
" on='Pty Indice',\n",
" how='left'\n",
" )\n",
"\n",
" # Calculate 'IDD Marge Standard (unit)' and 'IDD Sale Price' for these rows\n",
" df_snapshot_missing_updated['IDD Marge Standard (unit)'] = (\n",
" (df_snapshot_missing_updated['Currency turnover ex.VAT'] - df_snapshot_missing_updated['Standard amount USD']) / \n",
" df_snapshot_missing_updated['Quantity']\n",
" )\n",
" \n",
" df_snapshot_missing_updated['IDD Sale Price'] = (\n",
" df_snapshot_missing_updated['Currency turnover ex.VAT'] / df_snapshot_missing_updated['Quantity']\n",
" )\n",
"\n",
" # Update the specific rows in df_snapshot that are in df_snapshot_missing\n",
" df_snapshot.loc[df_snapshot['Pty Indice'].isin(df_snapshot_missing_updated['Pty Indice']), \n",
" ['IDD Marge Standard (unit)', 'IDD Sale Price']] = df_snapshot_missing_updated[\n",
" ['IDD Marge Standard (unit)', 'IDD Sale Price']].values\n",
"\n",
" print(f\"{num_rows_to_update} rows from df_snapshot updated with df_Historic\")\n",
"\n",
" '''\n",
" # Set 'Top-Level Status' to 'Completed - No Backlog' for the PN part of df_snapshot_missing only if 'IDD Sale Price' > 0 & only if Remain. crit. Qty = 0 \n",
" df_snapshot.loc[\n",
" (df_snapshot['Pty Indice'].isin(df_snapshot_missing['Pty Indice'])) &\n",
" (df_snapshot['IDD Sale Price'] > 0), \n",
" 'Top-Level Status'\n",
" ] = 'Completed - No Backlog'\n",
"\n",
" # Print the number of rows updated\n",
" print(f\"{num_rows_to_update} rows from df_snapshot updated with df_Historic\")\n",
" '''\n",
" \n",
" ##########################################################################################\n",
" # Set 'Top-Level Status' to 'Completed - No Backlog' or 'Not completed - No Backlog'\n",
" ##########################################################################################\n",
" # Condition 1: Set 'Completed - No Backlog' if 'IDD Sale Price' > 0 and 'Remain. crit. Qty' = 0\n",
" rows_to_update_completed = (\n",
" (df_snapshot['Pty Indice'].isin(df_snapshot_missing['Pty Indice'])) &\n",
" (df_snapshot['IDD Sale Price'] > 0) &\n",
" (df_snapshot['Remain. crit. Qty'] == 0)\n",
" )\n",
" \n",
" df_snapshot.loc[rows_to_update_completed, 'Top-Level Status'] = 'Completed - No Backlog'\n",
" \n",
" # Condition 2: Set 'Not completed - No Backlog' if 'IDD Sale Price' > 0 and 'Remain. crit. Qty' > 0\n",
" rows_to_update_not_completed = (\n",
" (df_snapshot['Pty Indice'].isin(df_snapshot_missing['Pty Indice'])) &\n",
" (df_snapshot['IDD Sale Price'] > 0) &\n",
" (df_snapshot['Remain. crit. Qty'] > 0)\n",
" )\n",
" \n",
" df_snapshot.loc[rows_to_update_not_completed, 'Top-Level Status'] = 'Not completed - No Backlog'\n",
" \n",
" # Print the number of rows updated for each condition\n",
" num_rows_completed = rows_to_update_completed.sum()\n",
" num_rows_not_completed = rows_to_update_not_completed.sum()\n",
" \n",
" print(f\"{num_rows_completed} rows updated with 'Completed - No Backlog'\")\n",
" print(f\"{num_rows_not_completed} rows updated with 'Not completed - No Backlog'\")\n",
"\n",
"############################\n",
"# Create column '% Margin'\n",
"###########################\n",
"#New code 07/24/24\n",
"# Fill missing values if needed\n",
"'''\n",
"df_snapshot['IDD Marge Standard (unit)'].fillna(0, inplace=True)\n",
"df_snapshot['IDD Sale Price'].fillna(0, inplace=True)\n",
"''' \n",
"# Replaced 09/23 to avoid warning \n",
"df_snapshot['IDD Marge Standard (unit)'] = df_snapshot['IDD Marge Standard (unit)'].fillna(0)\n",
"df_snapshot['IDD Sale Price'] = df_snapshot['IDD Sale Price'].fillna(0)\n",
"\n",
"\n",
"# Calculate '% Margin' with handling for division by zero - Updated 08/16, this return inacurates values\n",
"df_snapshot['IDD Current Margin (%)'] = df_snapshot.apply(\n",
" lambda row: round(row['IDD Marge Standard (unit)'] / row['IDD Sale Price'], 3) if row['IDD Sale Price'] != 0 else None,\n",
" axis=1\n",
")\n",
"\n",
"# Convert any `-0` to `0`\n",
"df_snapshot['IDD Current Margin (%)'] = df_snapshot['IDD Current Margin (%)'].apply(lambda x: 0 if x == -0 else x)\n",
"\n",
"#print('df_snapshot[IDD Current Margin (%)] BEFORE convertion to string')\n",
"#display(df_snapshot['IDD Current Margin (%)'])\n",
"\n",
"# Format '% Margin' as a percentage string with a '%' sign \n",
"df_snapshot['IDD Current Margin (%)'] = df_snapshot['IDD Current Margin (%)'].apply(lambda x: f\"{x * 100:.2f}%\" if pd.notna(x) else 'N/A')\n",
"\n",
"#print('df_snapshot[IDD Current Margin (%)] ATFER convertion to string')\n",
"#display(df_snapshot['IDD Current Margin (%)'])\n",
"\n",
"\n",
"###################################################################################################\n",
"# Create column 'Production Cost' as average of 'Actual amount -standard' from the current backlog \n",
"###################################################################################################\n",
"#New code 07/25\n",
"# Calculate 'Production Cost (unit)' for each row in df_backlog\n",
"df_backlog['IDD Production Cost (unit)'] = df_backlog.apply(\n",
" lambda row: round(row['Actual amount -standard'] / row['Backlog row Qty'], 1) if row['Backlog row Qty'] != 0 else None,\n",
" axis=1\n",
")\n",
"\n",
"# Group by 'IDD Top Level' and aggregate 'IDD Production Cost (unit)' if needed\n",
"# Here we assume you need the average or another aggregate measure\n",
"df_backlog_grouped = df_backlog.groupby('IDD Top Level').agg({\n",
" 'IDD Production Cost (unit)': 'mean' # Or use 'first' if you just need the first non-null value\n",
"}).reset_index()\n",
"\n",
"# Merge 'IDD Production Cost (unit)' back into df_snapshot\n",
"df_snapshot = pd.merge(df_snapshot, df_backlog_grouped, on='IDD Top Level', how='left')\n",
"\n",
"# If 'IDD production Cost (unit)' is NaN in df_snapshot, you may choose to fill with a default value or leave as is\n",
"df_snapshot.loc[:, 'IDD Production Cost (unit)'] = df_snapshot['IDD Production Cost (unit)'].fillna(0)\n",
"\n",
"# Display updated df_snapshot\n",
"#print(\"Updated df_snapshot with Production Cost:\")\n",
"#display(df_snapshot)\n",
"\n",
"\n",
"#--> \n",
"######################################################################################################################################################################\n",
"# WIP 19/17 - update for row not in backlog with df_Histroic - Update 09/23\n",
"########################################################################################################################################################################\n",
"# --> Keep most recent row on df_Historic and set df_snapshot['IDD Production Cost (unit)'] = df_Historic['Standard amount USD']/df_Historic['Quantity']\n",
"# For rows missing from backlog but present in df_Historic, calculate 'IDD Production Cost (unit)'\n",
"# Calculate 'IDD Production Cost (unit)' based on last row in df_Historic\n",
"df_Historic_mostrecent['IDD Production Cost (unit)'] = df_Historic_mostrecent['Standard amount USD'] / df_Historic_mostrecent['Quantity']\n",
"\n",
"# Filter df_Historic_mostrecent to include only the 'Pty Indice' present in df_snapshot_missing\n",
"df_Historic_mostrecent_filtered = df_Historic_mostrecent[df_Historic_mostrecent['Pty Indice'].isin(df_snapshot_missing['Pty Indice'])]\n",
"\n",
"# Create a mapping of 'Pty Indice' to 'IDD Production Cost (unit)'\n",
"production_cost_mapping = df_Historic_mostrecent_filtered.set_index('Pty Indice')['IDD Production Cost (unit)'].to_dict()\n",
"\n",
"# Update 'IDD Production Cost (unit)' in df_snapshot where 'Pty Indice' is in the production cost mapping\n",
"df_snapshot.loc[df_snapshot['Pty Indice'].isin(production_cost_mapping.keys()), 'IDD Production Cost (unit)'] = df_snapshot['Pty Indice'].map(production_cost_mapping)\n",
"\n",
"# Print the number of rows updated\n",
"print(f\"{len(df_snapshot[df_snapshot['Pty Indice'].isin(production_cost_mapping.keys())])} rows updated with production costs.\")\n",
"############################\n",
"# End WIP 09/17\n",
"############################\n",
"#--> \n",
"\n",
"#######################\n",
"# Create column 'ROI'\n",
"#######################\n",
"#New code 07/25/24\n",
"#'ROI' depend on the 'Production Status', the 'Product Category', IDD Marge Standard (unit) and 'Total critical Qty' which is 'Shipped' + 'Remain. crit. Qty'\n",
"\n",
"#Development Costs - Assuming no major problems:\n",
"# 80 Engineering Man-hours-Irvine: $8000\n",
"# 40 Engineering man-hours-Red: $4000\n",
"# Procurement: $600\n",
"# FTB – routings, PBS, ATP/ATR, etc: $4000\n",
"# Prototype First Time Build: $6000.\n",
"\n",
"#Define Engineering Cost based on 'Product Category' and 'Production Status'\n",
"Engineering_cost_Proto_FTB_RED = 14600\n",
"Engineering_cost_FTB_only_RED = 8600\n",
"\n",
"#######################################################################\n",
"# The Engineering Cost depend on ['Production Effort'] from CM-Priority \n",
"########################################################################\n",
"# Define a function to determine Engineering Cost based on 'Production Effort'\n",
"def determine_engineering_cost(production_effort):\n",
" if production_effort == 'Proto + FTB':\n",
" return Engineering_cost_Proto_FTB_RED\n",
" elif production_effort == 'FTB':\n",
" return Engineering_cost_FTB_only_RED\n",
" else:\n",
" \n",
" return 0 # Default value if no match\n",
"\n",
"########################################################################################################################\n",
"# Expected ROI - Based on total Crit. Qty or backlog whatever is the biggest\n",
"#######################################################################################################################\n",
"##################################\n",
"# Merge 'Critical Qty' from df_priority into df_snapshot\n",
"##################################\n",
"# Merge df_priority with df_snapshot\n",
"df_snapshot = pd.merge(df_snapshot, df_priority[['Pty Indice', 'Critical Qty', 'Production Effort']], on='Pty Indice', how='left')\n",
"\n",
"# Display the updated df_snapshot\n",
"#print(\"Updated df_snapshot with Production Effort:\")\n",
"#display(df_snapshot)\n",
"\n",
"#########################################################################################################\n",
"# Create 'IDD Manufacturing Cost (unit)' = 'IDD Production Cost (unit)' + 'IDD Engineering Cost (Unit)' \n",
"##########################################################################################################\n",
"# Ensure 'IDD Production Cost (unit)' is numeric\n",
"df_snapshot['IDD Production Cost (unit)'] = pd.to_numeric(df_snapshot['IDD Production Cost (unit)'], errors='coerce')\n",
"df_snapshot['Critical Qty'] = pd.to_numeric(df_snapshot['Critical Qty'], errors='coerce')\n",
"\n",
"# Define a function to calculate max_qty for each row\n",
"def calculate_max_qty(row):\n",
" # Handle possible NaN values\n",
" critical_qty = row.get('Critical Qty', 0)\n",
" backlog_qty = row.get('IDD Backlog Qty', 0)\n",
" \n",
" return max(row['Critical Qty'], row['IDD Backlog Qty'])\n",
"\n",
"# Apply the function to calculate max_qty and add it as a new column\n",
"df_snapshot['Max Qty'] = df_snapshot.apply(calculate_max_qty, axis=1)\n",
"\n",
"# Calculate 'Engineering Cost' and 'IDD Manufacturing Cost (unit)'\n",
"df_snapshot.loc[:, 'Engineering Cost'] = df_snapshot['Production Effort'].apply(determine_engineering_cost)\n",
"\n",
"# Calculate 'IDD Manufacturing Cost (unit)' using the Max Qty\n",
"#df_snapshot.loc[:, 'IDD Manufacturing Cost (unit)'] = df_snapshot['IDD Production Cost (unit)'] + (df_snapshot['Engineering Cost'] / df_snapshot['Max Qty'])\n",
"\n",
"# Convert columns to numeric, forcing errors to NaN\n",
"df_snapshot['Engineering Cost'] = pd.to_numeric(df_snapshot['Engineering Cost'], errors='coerce')\n",
"#df_snapshot['IDD Manufacturing Cost (unit)'] = pd.to_numeric(df_snapshot['IDD Production Cost (unit)'], errors='coerce')\n",
"\n",
"# Calculate total ROI directly\n",
"def calculate_total_roi(row):\n",
" # Ensure Engineering Cost is not zero to avoid division by zero\n",
" if row['Engineering Cost'] != 0:\n",
" # Calculate ROI as a percentage\n",
" roi = (row['IDD Marge Standard (unit)'] * row['Max Qty']) / row['Engineering Cost'] * 100\n",
" return roi\n",
" else:\n",
" return None\n",
" \n",
"# Apply the ROI calculation\n",
"df_snapshot['IDD Expected ROI (Total)'] = df_snapshot.apply(calculate_total_roi, axis=1)\n",
"\n",
"# Format ROI to two decimal places and append a percentage sign, or 'N/A' for NaN\n",
"df_snapshot['IDD Expected ROI (Total)'] = df_snapshot['IDD Expected ROI (Total)'].apply(\n",
" lambda x: f\"{round(x, 2)}%\" if pd.notna(x) else 'N/A'\n",
")\n",
"\n",
"# Remove 'Production Effort' and 'Engineering Cost' columns from df_snapshot\n",
"#df_snapshot.drop(columns=['Production Effort', 'Engineering Cost'], errors='ignore', inplace=True)\n",
"df_snapshot.drop(columns=['Production Effort', 'Max Qty'], errors='ignore', inplace=True)\n",
"\n",
"# Display the updated df_snapshot\n",
"#print(\"Updated df_snapshot with ROI:\")\n",
"#display(df_snapshot)\n",
"\n",
"#######################################################################\n",
"# Create column 'Qty WIP' for each 'IDD Top-Level' from tab |CM-WIP|\n",
"######################################################################\n",
"#New code 07/30\n",
"# Filter df_WIP where Level is 0\n",
"df_WIP_filtered = df_WIP[df_WIP['Level'] == 0].copy()\n",
"\n",
"# Convert 'WO' to string\n",
"df_WIP_filtered['WO'] = df_WIP_filtered['WO'].astype(str)\n",
"\n",
"# Replace string 'nan' with np.nan for better handling\n",
"#df_WIP_filtered['WO'].replace('nan', np.nan, inplace=True) # Replaced 09/24 to avoid warning\n",
"df_WIP_filtered['WO'] = df_WIP_filtered['WO'].replace('nan', np.nan) \n",
"\n",
"# Drop rows where 'WO' is NaN or empty\n",
"df_WIP_filtered = df_WIP_filtered.dropna(subset=['WO'])\n",
"df_WIP_filtered = df_WIP_filtered[df_WIP_filtered['WO'].str.strip() != '']\n",
"\n",
"# Drop rows where 'WO' contains 'NC'\n",
"df_WIP_filtered = df_WIP_filtered[~df_WIP_filtered['WO'].str.contains('NC')]\n",
"\n",
"# Drop duplicate rows based on 'WO' + 'Pty Indice'\n",
"df_WIP_filtered = df_WIP_filtered.drop_duplicates(subset=['WO', 'Pty Indice'])\n",
"\n",
"# Display 'Pty Indice' and 'WO' values in the filtered DataFrame\n",
"#print('display df_WIP_filtered')\n",
"#display(df_WIP_filtered[['Pty Indice', 'WO', 'Qty Ordered']])\n",
"\n",
"# Sum 'Qty Ordered' for each unique 'Pty Indice'\n",
"df_WIP_aggregated = df_WIP_filtered.groupby('Pty Indice')['Qty Ordered'].sum().reset_index()\n",
"\n",
"# Rename 'Qty Ordered' to 'Qty WIP' for clarity\n",
"df_WIP_aggregated.rename(columns={'Qty Ordered': 'Qty WIP'}, inplace=True)\n",
"\n",
"# Fill 'Qty WIP' with 0 when NaN - Does not work\n",
"#df_WIP_aggregated['Qty WIP'].fillna(0, inplace=True) # # Replaced 09/24 to avoid warning\n",
"df_WIP_aggregated['Qty WIP'] = df_WIP_aggregated['Qty WIP'].fillna(0)\n",
"\n",
"# Merge the aggregated data with df_snapshot\n",
"df_snapshot = pd.merge(df_snapshot, df_WIP_aggregated, on='Pty Indice', how='left')\n",
"\n",
"# Fill 'Qty WIP' with 0 when NaN - Introduced 08/21\n",
"#df_snapshot['Qty WIP'].fillna(0, inplace=True) # Replaced 09/24 to avoid warning\n",
"df_snapshot['Qty WIP'] = df_snapshot['Qty WIP'].fillna(0)\n",
"\n",
"# Print the results\n",
"#print('df_WIP_filtered including only unique WO+Pty Indice without NC:')\n",
"#display(df_WIP_filtered)\n",
"\n",
"#print('df_WIP_aggregated with summed Qty WIP for unique WO+Pty Indice per IDD Top Level:')\n",
"#display(df_WIP_aggregated)\n",
"\n",
"#New 08/14\n",
"############################################################################################\n",
"# Create column 'Program' with mapping from df_Priority on Pty Indice\n",
"#############################################################################################\n",
"program_mapping = df_Priority.set_index('Pty Indice')['Program'].to_dict()\n",
"df_snapshot['Program'] = df_snapshot['Pty Indice'].map(program_mapping)\n",
"\n",
"#print('df_snapshot BEFORE inclding new columns')\n",
"#display(df_snapshot)\n",
"\n",
"#New 08/22\n",
"###################################################################################################################################################\n",
"# Create column 'Avg Expected Time (unit)', 'Avg Actual Time (unit)', 'WO_Count' and 'Actual vs Expected time (%)' based on |CM-MakeArchitecture|\n",
"####################################################################################################################################################\n",
"# Create df_MakeArchi from df_Make_architure_final_sorted\n",
"df_MakeArchi = df_Make_architure_final_sorted.copy()\n",
"\n",
"# The 'BOM Qty' needs to be taken into account in the calculation 'Avg Actual Time (unit)[hour]'*'BOM Qty'* for a given 'IDD Component'\n",
"# Group by 'Pty Indice' and calculate the required aggregations\n",
"df_aggregated_MakeArchi = df_MakeArchi.groupby('Pty Indice').agg(\n",
" Max_Expected_Time=('Max Expected Time (unit)[hour]', 'sum'), \n",
" Sum_Weighted_Actual_Time=('Avg Actual Time (unit)[hour]', lambda x: (x * df_MakeArchi.loc[x.index, 'BOM Qty']).sum()),\n",
" WO_Count=('WO_Count', 'sum'),\n",
" Max_Standard_dev=('Standard Deviation [hour]', 'max'), #New 08/26\n",
").reset_index()\n",
"\n",
"# Rename columns to match the desired format\n",
"df_aggregated_MakeArchi.rename(columns={\n",
" 'Max_Expected_Time': 'Max Expected Time (full ASSY)[hour]',\n",
" 'Sum_Weighted_Actual_Time': 'Avg Actual Time (full ASSY)[hour]',\n",
" 'Max_Standard_dev': 'Max Standard Deviation [hour]', #New 08/26\n",
" 'WO_Count': 'Total WO Count',\n",
"}, inplace=True)\n",
"\n",
"# Round the 'Max Expected Time (unit)' and 'Avg Actual Time (unit)' columns to 2 decimal places\n",
"df_aggregated_MakeArchi['Max Expected Time (full ASSY)[hour]'] = df_aggregated_MakeArchi['Max Expected Time (full ASSY)[hour]'].round(2)\n",
"df_aggregated_MakeArchi['Avg Actual Time (full ASSY)[hour]'] = df_aggregated_MakeArchi['Avg Actual Time (full ASSY)[hour]'].round(2)\n",
"\n",
"#print('df_aggregated_MakeArchi')\n",
"#display(df_aggregated_MakeArchi)\n",
"\n",
"# Merge aggregated data into df_MakeArchi\n",
"df_snapshot = df_snapshot.merge(\n",
" df_aggregated_MakeArchi,\n",
" on='Pty Indice',\n",
" how='left',\n",
" suffixes=('', '_aggregated')\n",
")\n",
"\n",
"\n",
"# Update 09/25\n",
"df_snapshot['Actual vs Standard time [%]'] = np.where(\n",
" df_snapshot['Max Expected Time (full ASSY)[hour]'].fillna(0) == 0,\n",
" 'N/A', # Handle division by zero\n",
" (\n",
" (((df_snapshot['Avg Actual Time (full ASSY)[hour]'] / \n",
" df_snapshot['Max Expected Time (full ASSY)[hour]']) - 1) * 100)\n",
" .replace([np.inf, -np.inf], np.nan) # Replace infinity values\n",
" .fillna(0) # Replace NaN values with 0\n",
" .round(0) # Round the result\n",
" .astype(int) # Convert to integer\n",
" .astype(str) + '%' # Convert to string and append '%'\n",
" )\n",
")\n",
"\n",
"# New 09/25 \n",
"# Fill 'Max Standard Deviation [hour]' with 0 if empty and 'Total WO Count' = 1\n",
"df_snapshot['Max Standard Deviation [hour]'] = np.where((df_snapshot['Max Standard Deviation [hour]'].isna()) & (df_snapshot['Total WO Count'] == 1), 0, df_snapshot['Max Standard Deviation [hour]']) # Keep the original values if condition not met\n",
"\n",
"\n",
"# Print the final DataFrame to verify results\n",
"#print('df_snapshot')\n",
"#display(df_snapshot)\n",
"\n",
"########################################################\n",
"# New 09/24 - Create new column 'Deviation vs Actual [%]'\n",
"########################################################\n",
"df_snapshot['Deviation vs Actual [%]'] = np.where(\n",
" df_snapshot['Max Standard Deviation [hour]'].fillna(0) == 0, \n",
" 'N/A', # Handle division by zero\n",
" (\n",
" ((df_snapshot['Max Standard Deviation [hour]'] / \n",
" df_snapshot['Avg Actual Time (full ASSY)[hour]']) * 100)\n",
" .replace([np.inf, -np.inf], np.nan) # Replace infinity values\n",
" .fillna(0) # Replace NaN values with 0\n",
" .round(0) # Round the result\n",
" .astype(int) # Convert to integer\n",
" .astype(str) + '%' # Convert to string and append '%'\n",
" )\n",
")\n",
"\n",
"#New 09/20\n",
"#######################################################################################################################################################################################\n",
"# Create news columns 'Gap Actual vs Standard [USD]', 'IDD Corrected Margin Standard (unit)[USD]', 'IDD Corrected Cost [USD]' & 'IDD Corrected Margin [%]' \n",
"# based on the Direct labor rate, Burden (indirect rate) = Fully burdened rate\n",
"####################################################################################################################################################################################\n",
"# To calculate the impact in USD of the differences between standard time and actual time, the formula is: (Actual time − Standard time) × labor + burden rate (Actual time−Standard time)×labor + burden rate\n",
"# The rate usually depends on where the part is produced. For example, if it's produced in the 6444 location, the CPA rate applies, but for other locations, there will be a different rate.\n",
"# By default, let's use the global rate for Redmond, which is $79.58 for 2024 : Direct labor rate: $41.20, Burden (indirect rate): $38.38, Fully burdened rate: $79.58.\n",
"################################################################################################################################################################################\n",
"# df_snapshot['Max Expected Time (full ASSY)[hour]'] represents the most recent 'Standard Time' of the full ASSY \n",
"# df_snapshot['Avg Actual Time (full ASSY)[hour]'] represents the all time AVG 'Actual Time' with the filtering function applied; abberante values has been filtered-out \n",
"# df_snapshot['IDD Production Cost (unit)'] represents the current labor cost used to calculate the Margin \n",
"# df_snapshot['IDD Sale Price'] represents the sales price \n",
"\n",
"# Fully_Burden_Rate_2024 represents the labor cost - Define the Fully burdened rate in USD\n",
"Fully_Burden_Rate_2024 = 79.58\n",
"\n",
"# Calculate df_snapshot['Gap Actual vs Standard [USD]'] = (df_snapshot['Avg Actual Time (full ASSY)[hour]'] - df_snapshot['Max Expected Time (full ASSY)[hour]'])*Fully_Burden_Rate_2024\n",
"df_snapshot['Gap Actual vs Standard [USD]'] = (\n",
" df_snapshot['Avg Actual Time (full ASSY)[hour]'] - df_snapshot['Max Expected Time (full ASSY)[hour]']\n",
") * Fully_Burden_Rate_2024\n",
"\n",
"# Calculate df_snapshot['IDD Corrected Cost [USD]'] = df_snapshot['IDD Production Cost (unit)'] + df_snapshot['Gap Actual vs Standard [USD]'] \n",
"df_snapshot['IDD Corrected Cost [USD]'] = (\n",
" df_snapshot['IDD Production Cost (unit)'] + df_snapshot['Gap Actual vs Standard [USD]']\n",
")\n",
"\n",
"# Calculate df_snapshot['IDD Corrected Margin Standard (unit)[USD]'] = df_snapshot['IDD Sale Price'] - df_snapshot['IDD Corrected Cost [USD]']\n",
"df_snapshot['IDD Corrected Margin Standard (unit) [USD]'] = (\n",
" df_snapshot['IDD Sale Price'] - df_snapshot['IDD Corrected Cost [USD]']\n",
")\n",
"\n",
"#Calculate df_snapshot['IDD Corrected Margin [%]'] = df_snapshot['IDD Sale Price'] - df_snapshot['IDD Corrected Cost [USD]'] with handling for division by zero - Updated 08/16, this return inacurates values\n",
"df_snapshot['IDD Corrected Margin [%]'] = df_snapshot.apply(\n",
" lambda row: round(row['IDD Corrected Margin Standard (unit) [USD]'] / row['IDD Sale Price'], 3) \n",
" if row['IDD Sale Price'] != 0 else None, \n",
" axis=1\n",
")\n",
"\n",
"# Convert any `-0` to `0`\n",
"df_snapshot['IDD Corrected Margin [%]'] = df_snapshot['IDD Corrected Margin [%]'].apply(lambda x: 0 if x == -0 else x)\n",
"\n",
"# Format '% Margin' as a percentage string with a '%' sign \n",
"df_snapshot['IDD Corrected Margin [%]'] = df_snapshot['IDD Corrected Margin [%]'].apply(\n",
" lambda x: f\"{x * 100:.2f}%\" if pd.notna(x) else 'N/A'\n",
")\n",
"\n",
"\n",
"# Round and add $ sign to 'Gap Actual vs Standard [USD]', 'IDD Corrected Cost [USD]', 'IDD Corrected Margin Standard (unit) [USD]'\n",
"df_snapshot['Gap Actual vs Standard [USD]'] = df_snapshot['Gap Actual vs Standard [USD]'].apply(lambda x: f\"${round(x, 2):,.2f}\" if pd.notna(x) else 'N/A')\n",
"df_snapshot['IDD Corrected Cost [USD]'] = df_snapshot['IDD Corrected Cost [USD]'].apply(lambda x: f\"${round(x, 2):,.2f}\" if pd.notna(x) else 'N/A')\n",
"df_snapshot['IDD Corrected Margin Standard (unit) [USD]'] = df_snapshot['IDD Corrected Margin Standard (unit) [USD]'].apply(lambda x: f\"${round(x, 2):,.2f}\" if pd.notna(x) else 'N/A')\n",
"\n",
"\n",
"######################################################################################################################\n",
"## New 09/23 #####################\n",
"######################################################################################################################\n",
"# --> Updated 09/23 to use df_Historic instead of df_Snapshot to calculate the 'Realized sales' and 'Realized Margin' \n",
"# The calculation should be based on the real data from the df_Historic trunover Report including the change of price over time \n",
"# Need to create 'IDD AVG realized sales price [USD]' & ['IDD AVG realized Margin[%]']\n",
"######################################################################################################################\n",
"# df_snapshot['IDD AVG realized sales price [USD]'] = Average of (df_Historic['Currency turnover ex.VAT']/df_Historic['Quantity']) for a given Pty Indice \n",
"# df_snapshot['IDD AVG realized Margin Standard [USD]'] = Average of (df_Historic['Currency turnover ex.VAT'] - df_Historic['Standard amount USD'])/['Quantity']\n",
"# df_snapshot['IDD AVG realized Margin [%]'] = df_snapshot['IDD AVG realized Margin Standard [USD]']/['IDD AVG realized sales price [USD]']*100\n",
"\n",
"# 1. Filter out rows where 'Order' contains 'NC'\n",
"df_Historic_filtered = df_Historic[~df_Historic['Order'].str.contains('NC', na=False)].copy()\n",
"\n",
"# 2. Calculate the average realized sales price for each 'Pty Indice'\n",
"df_Historic_filtered.loc[:, 'Realized Sales Price [USD]'] = df_Historic_filtered['Currency turnover ex.VAT'] / df_Historic_filtered['Quantity']\n",
"avg_realized_sales_price = df_Historic_filtered.groupby('Pty Indice')['Realized Sales Price [USD]'].mean().reset_index()\n",
"\n",
"# 3. Calculate the average realized margin standard [USD]\n",
"df_Historic_filtered.loc[:, 'Realized Margin Standard [USD]'] = (\n",
" (df_Historic_filtered['Currency turnover ex.VAT'] - df_Historic_filtered['Standard amount USD']) / df_Historic_filtered['Quantity']\n",
")\n",
"avg_realized_margin_standard = df_Historic_filtered.groupby('Pty Indice')['Realized Margin Standard [USD]'].mean().reset_index()\n",
"\n",
"# 4. Merge the average sales price and margin back to df_snapshot\n",
"df_snapshot = df_snapshot.merge(avg_realized_sales_price, on='Pty Indice', how='left')\n",
"df_snapshot = df_snapshot.merge(avg_realized_margin_standard, on='Pty Indice', how='left')\n",
"\n",
"# Rename columns for clarity\n",
"df_snapshot.rename(columns={\n",
" 'Realized Sales Price [USD]': 'IDD AVG realized sales price [USD]',\n",
" 'Realized Margin Standard [USD]': 'IDD AVG realized Margin Standard [USD]'\n",
"}, inplace=True)\n",
"\n",
"# 5. Calculate the realized margin [%]\n",
"df_snapshot['IDD AVG realized Margin [%]'] = (\n",
" df_snapshot['IDD AVG realized Margin Standard [USD]'] / df_snapshot['IDD AVG realized sales price [USD]'] * 100\n",
").fillna(0) # Fill NaN with 0 if there's no sales price\n",
"\n",
"# 6. Format the results\n",
"df_snapshot['IDD AVG realized sales price [USD]'] = df_snapshot['IDD AVG realized sales price [USD]'].apply(lambda x: f\"${round(x, 2):,.2f}\" if pd.notna(x) else 'N/A')\n",
"df_snapshot['IDD AVG realized Margin Standard [USD]'] = df_snapshot['IDD AVG realized Margin Standard [USD]'].apply(lambda x: f\"${round(x, 2):,.2f}\" if pd.notna(x) else 'N/A')\n",
"df_snapshot['IDD AVG realized Margin [%]'] = df_snapshot['IDD AVG realized Margin [%]'].apply(lambda x: f\"{round(x, 2):,.2f}%\" if pd.notna(x) else 'N/A')\n",
"\n",
"\n",
"######################################################################################################################\n",
"# New 10/14 - Update 'Remain. crit. Qty' if different from |CM-Priority| in order consider the unit shipped recently. \n",
"# 'Remain. crit. Qty' is set based on |CM-Inventory| which won't be update until the new generation of inputs\n",
"# To have a corrected 'Remain. crit. Qty' in |Snapshot|, the value has to be update based on |CM-priority|\n",
"######################################################################################################################\n",
"# Create a mapping from on 'Pty Indice' with the updated |CM-Priority|\n",
"remaining_qty_mapping = df_Priority.set_index('Pty Indice')['Remain. crit. Qty'].to_dict()\n",
"\n",
"# Update 'Remain. crit. Qty' only if different from the current value in df_snapshot\n",
"df_snapshot['Remain. crit. Qty'] = df_snapshot.apply(\n",
" lambda row: remaining_qty_mapping.get(row['Pty Indice'], row['Remain. crit. Qty']) \n",
" if row['Remain. crit. Qty'] != remaining_qty_mapping.get(row['Pty Indice'], row['Remain. crit. Qty']) \n",
" else row['Remain. crit. Qty'], axis=1\n",
")\n",
"\n",
"#***********************************************************************************************************\n",
"######################################################################################################################\n",
"### *** 4 PN rule maximum: Rule to be applied to both 'Start target date' & 'Industrialization target date' ***\n",
"####################################################################################################################\n",
"#***********************************************************************************************************\n",
"#########################\n",
"# Sort the final dataframe by 'Pty Indice' and 'Priority'\n",
"#########################\n",
"df_snapshot = df_snapshot.sort_values(by=['Priority','Pty Indice'])\n",
"\n",
"#####################################################\n",
"######### Creating SNAPSHOT ####################\n",
"#####################################################\n",
"# Check if \"Snapshot\" sheet already exists\n",
"if \"Snapshot\" in workbook.sheetnames:\n",
" # Remove the existing \"Snapshot\" sheet\n",
" workbook.remove(workbook[\"Snapshot\"])\n",
"\n",
"# Create a new \"snapshot\" sheet\n",
"snapshot_sheet = workbook.create_sheet(title='Snapshot', index=0) # Add as the 1st sheet (index 0)\n",
"\n",
"# Write headers\n",
"for c_idx, header in enumerate(df_snapshot.columns, start=1):\n",
" snapshot_sheet.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Write data\n",
"for r_idx, row in enumerate(df_snapshot.values, start=2): # Start from row 2\n",
" for c_idx, value in enumerate(row, start=1):\n",
" snapshot_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"# Save the updated workbook\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"Snapshot added successfully as |Snapshot| in {original_input}\")\n",
"\n",
"\n",
"#*********************************************************************************************************************************************\n",
"# UPDATE of CM-Priority based of SUMMARY and SNAPSHOT\n",
"#*********************************************************************************************************************************************\n",
"#sheet_CTB = workbook['Clear-to-Build']\n",
"#sheet_snapshot = workbook['Snapshot']\n",
"#####################################################################\n",
"### Filling |Clear to build| from CM-Priority column based on SUMARRY \n",
"#####################################################################\n",
"# Fill Top Level sharing component\n",
"\n",
"#####################################################################\n",
"### Filling |Clear to build| from CM-Priority column based on SNAPSHOT \n",
"#####################################################################\n",
"#Fill Clear to build & Start date target \n",
"#print(f\"CM-Priority successfully updated in based on |Summary| and |Snapshot| in {original_input}\")\n",
"\n",
"#/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"########################################################################################################################\n",
"#################### --->>> FORMATING SUMMARY & SNAPSHOT ######################\n",
"#######################################################################################################################\n",
"#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"##### load the formatted workbook\n",
"workbook = load_workbook(original_input) \n",
"# Define sheet_summary\n",
"sheet_summary = workbook['Summary']\n",
"max_row_CTB = sheet_summary.max_row\n",
"\n",
"######################################\n",
"###Formatting tab 1 sheet_summary ###\n",
"######################################\n",
"# Last update column P\n",
"content_P_CTB = {\n",
" 1: \"Last update\",\n",
" 2: file_date_inventory, # Using the datetime object directly\n",
" 3: \"Summary report: Based on IDD's Inventory & existing BOM\"\n",
"}\n",
"\n",
"# Write the date to a specific cell\n",
"sheet_summary.cell(row=1, column=16, value='Last Update') # P2 cell\n",
"sheet_summary.cell(row=2, column=16, value=file_date_inventory) # P2 cell\n",
"\n",
"# Set column widths and text alignment for columns A to O)\n",
"for column in sheet_summary.iter_cols(min_col=1, max_col=15):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['A', 'C', 'D', 'E','I','J','K','L','M', 'N']:\n",
" sheet_summary.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['B','H', 'O']:\n",
" sheet_summary.column_dimensions[column_letter].width = 30\n",
" elif column_letter in ['Q']:\n",
" sheet_summary.column_dimensions[column_letter].width = 35\n",
" else:\n",
" sheet_summary.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"\n",
"# Set column widths and text alignment for columns P\n",
"for column in sheet_summary.iter_cols(min_col=16, max_col=16):\n",
" for cell in column:\n",
" cell.alignment = Alignment(horizontal='left' if cell.row == 1 else 'center', vertical='center')\n",
" \n",
" if column[0].column_letter == 'P':\n",
" sheet_summary.column_dimensions['P'].width = 15\n",
"\n",
"# Set left alignment for column O\n",
"for row in sheet_summary.iter_rows(min_row=1, max_row=sheet_summary.max_row, min_col=15, max_col=15): # Column O is the 15th column\n",
" for cell in row:\n",
" cell.alignment = Alignment(horizontal='right')\n",
" \n",
"# Set column width and text alignment for column P from row 3\n",
"for cell in sheet_summary.iter_rows(min_row=3, min_col=16, max_col=16):\n",
" cell[0].alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Apply borders for rows 1 and 2 in column P\n",
"for row in range(1, 3):\n",
" sheet_summary.cell(row=row, column=16).border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Apply conditional formatting for 'Max Qty (GS)' column when equal to 0\n",
"# Define the red fill pattern\n",
"fill_red = PatternFill(start_color='FFC7CE', end_color='FFC7CE', fill_type='solid')\n",
"\n",
"# Loop through the rows starting from row 2 to max_row_CTB\n",
"for row in range(2, max_row_CTB + 1): # Assuming data starts from row 2\n",
" max_qty_cell = sheet_summary.cell(row=row, column=13) # 'Max Qty (GS)' is in column 13\n",
" if max_qty_cell.value == 0:\n",
" # Apply red fill to the 'Max Qty (GS)' cell\n",
" max_qty_cell.fill = fill_red\n",
" # Apply red fill to the cell in 'idd_component' column, which is column 5\n",
" idd_component_cell = sheet_summary.cell(row=row, column=5)\n",
" idd_component_cell.fill = fill_red\n",
"\n",
"#######################################################################################################\n",
"### Apply conditional formatting for 'Make Part' & 'Make Part CUU' in 'Supplier' column - Upadte 09/20\n",
"#######################################################################################################\n",
"# Define light grey fill\n",
"fill_grey = PatternFill(start_color='E0E0E0', end_color='E0E0E0', fill_type='solid')\n",
"\n",
"# Apply conditional formatting for 'Make Part' in 'Supplier' column (columns B to O)\n",
"for row in range(2, max_row_CTB + 1): # Assuming data starts from row 2\n",
" supplier_cell = sheet_summary.cell(row=row, column=2) # Assuming 'Supplier' column is in column B (2)\n",
" if supplier_cell.value and 'Make Part' in supplier_cell.value:\n",
" for col in range(2, 15): # Columns B to O (2 to 15)\n",
" cell = sheet_summary.cell(row=row, column=col)\n",
" cell.fill = fill_grey\n",
" \n",
"##########################################################################\n",
"# Apply conditional formatting for row dedicated to Top-Level (Level = 0)\n",
"##########################################################################\n",
"# Define fill color for Top-Level\n",
"fill_TopLevel = PatternFill(start_color='5B9BD5', end_color='5B9BD5', fill_type='solid') # Blue fill\n",
"\n",
"# Define font colors\n",
"font_color_clear = \"FFFFFF\" # White\n",
"font_color_shortage = \"C00000\" # Dark Red\n",
"font_color_fullyClear = \"24CA77\" # Green\n",
"\n",
"# Define thick border\n",
"thick_border = Border(\n",
" top=Side(style='thick'),\n",
" bottom=Side(style='thick')\n",
")\n",
"\n",
"# Iterate through each row in the sheet\n",
"for row in range(2, max_row_CTB + 1):\n",
" cell_level = sheet_summary.cell(row=row, column=6) # Assuming 'Level' is in column 6 (F)\n",
" cell_max_qty = sheet_summary.cell(row=row, column=14) # Assuming 'Max Qty Top-Level' is in column 14 (N)\n",
" cell_remain_crit_qty = sheet_summary.cell(row=row, column=12) # Assuming 'Remain. crit. Qty' is in column 12 (L)\n",
" \n",
" if cell_level.value == 0:\n",
" # Determine the font color based on the value of 'Max Qty Top-Level' and 'Remain. crit. Qty'\n",
" font_color = font_color_clear # Default font color\n",
" if cell_max_qty.value is not None and cell_remain_crit_qty.value is not None:\n",
" if cell_max_qty.value >= cell_remain_crit_qty.value:\n",
" font_color = font_color_fullyClear\n",
" elif cell_max_qty.value > 0:\n",
" font_color = font_color_clear\n",
" elif cell_max_qty.value == 0:\n",
" font_color = font_color_shortage\n",
"\n",
" # Apply fill color, font color, bold, and border to each cell in the row\n",
" for col in range(1, 16):\n",
" cell = sheet_summary.cell(row=row, column=col)\n",
" cell.fill = fill_TopLevel\n",
" cell.font = Font(color=font_color, bold=True)\n",
" cell.border = thick_border\n",
"\n",
"#########################################################\n",
"# Applying formating to most critical shortage component \n",
"#######################################################\n",
"light_purple_fill = PatternFill(start_color='E4DFEC', end_color='E4DFEC', fill_type='solid')\n",
"\n",
"# 'Max Qty (GS)' column M, 'Max Qty Top-Level' column N --> Apply purple fill from column B to column M\n",
"for row in range(2, max_row_CTB + 1):\n",
" max_qty_gs = sheet_summary.cell(row=row, column=13).value\n",
" max_qty_top_level = sheet_summary.cell(row=row, column=14).value\n",
"\n",
" # Update 08/08\n",
" ''' \n",
" if max_qty_gs == max_qty_top_level and max_qty_top_level > 0:\n",
" for col in range(2, 14): # Columns B to M (2 to 13)\n",
" cell = sheet_summary.cell(row=row, column=col)\n",
" cell.fill = light_purple_fill # Assuming green_fill is defined earlier\n",
" '''\n",
" if max_qty_gs is not None and max_qty_top_level is not None and max_qty_gs == max_qty_top_level and max_qty_top_level > 0:\n",
" for col in range(2, 14): # Columns B to M (2 to 13)\n",
" cell = sheet_summary.cell(row=row, column=col)\n",
" cell.fill = light_purple_fill # Assuming green_fill is defined earlier\n",
" \n",
"######################################################################################\n",
"# Custom conditional formatting for column F 'Level' (Assuming data starts from row 2)\n",
"#####################################################################################\n",
"min_row = 2\n",
"col_F = 6\n",
"\n",
"# Initialize fill_color outside of the loop\n",
"fill_color = None\n",
"font_color_black = \"000000\" # Black\n",
"\n",
"# Iterate through each row in the specified range\n",
"for row in range(min_row, max_row_CTB + 1):\n",
" cell_value = sheet_summary.cell(row=row, column=col_F).value\n",
" \n",
" # Apply fill color based on the 'Level' value\n",
" if cell_value is not None:\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" else:\n",
" fill_color = None # Reset fill_color when cell value is None\n",
" \n",
" # Apply font color to column F (Level) in each row\n",
" sheet_summary.cell(row=row, column=col_F).font = Font(color=font_color_black, bold=False)\n",
" \n",
" # Check if fill_color is not None before applying PatternFill\n",
" if fill_color is not None:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')\n",
" sheet_summary.cell(row=row, column=col_F).fill = fill\n",
"\n",
"###################################################################\n",
"### Apply conditional formatting for 'Top-Level Status' column\n",
"###################################################################\n",
"fill_green = PatternFill(start_color='C6EFCE', end_color='C6EFCE', fill_type='solid') # Green fill\n",
"fill_red = PatternFill(start_color='FFC7CE', end_color='FFC7CE', fill_type='solid') # Red fill\n",
"fill_dark_green = PatternFill(start_color='6FAC46', end_color='6FAC46', fill_type='solid') # Dark green fill\n",
"fill_orange = PatternFill(start_color='ED7D31', end_color='ED7D31', fill_type='solid') # orange fill\n",
"\n",
"for row in range(2, max_row_CTB + 1):\n",
" cell = sheet_summary.cell(row=row, column=1) # Assuming 'Top-Level Status' is in column 1 (A)\n",
" if cell.value == 'Clear-to-Build':\n",
" cell.fill = fill_green\n",
" if cell.value == 'Completed - No Backlog':\n",
" cell.fill = fill_dark_green\n",
" if cell.value == 'Not completed - No Backlog':\n",
" cell.fill = fill_orange\n",
" elif cell.value == 'Shortage':\n",
" cell.fill = fill_red\n",
" \n",
"###################################################################\n",
"# Set background color for cell P2 and P3 left align\n",
"###################################################################\n",
"sheet_summary.cell(row=2, column=16).fill = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid')\n",
"\n",
"################################################################################################\n",
"# Create a new column [Comment] to identified potential interesting fact for a given component\n",
"###############################################################################################\n",
"#New 08/30 - Integrate a column 'BOM Index' \n",
"# Step 1: Create a dictionary to map IDD Component to Inventory Status and Qty On Hand\n",
"idd_to_inventory_status_qty = {row['IDD Component']: (row['Inventory Status'], int(row['Qty On Hand']))\n",
" for _, row in Inventory.iterrows()}\n",
"\n",
"# Step 2: Merge df_summary_sorted with CM_BOM to get the BOM Index\n",
"df_summary_sorted_with_bom_index = df_summary_sorted.merge(\n",
" CM_BOM[['IDD Component', 'Pty Indice', 'BOM Index']],\n",
" on=['IDD Component', 'Pty Indice'],\n",
" how='left'\n",
")\n",
"\n",
"# Define the starting column index and headers for the [Comment] and [BOM Index] columns\n",
"comment_start_column_index = sheet_summary.max_column + 1 # Place the comment column to the right of the last column\n",
"bom_index_column_index = comment_start_column_index + 1 # Place the BOM Index column right after the comment column\n",
"comment_header = 'Comment'\n",
"bom_index_header = 'BOM Index'\n",
"\n",
"# Step 3: Iterate through df_summary_sorted_with_bom_index to populate comments and BOM Index\n",
"comments = []\n",
"bom_indices = []\n",
"for idx, row in df_summary_sorted_with_bom_index.iterrows():\n",
" idd_component = row['IDD Component']\n",
" if idd_component in idd_to_inventory_status_qty:\n",
" inventory_status, qty_on_hand = idd_to_inventory_status_qty[idd_component]\n",
" if inventory_status in ['R-INSP', 'RTV', 'MRB']:\n",
" comment = f\"({qty_on_hand}) component(s) in inventory, status: {inventory_status}\"\n",
" else:\n",
" comment = \"\"\n",
" else:\n",
" comment = \"\"\n",
" comments.append(comment)\n",
" \n",
" # Get the BOM Index from the merged DataFrame\n",
" bom_index = row['BOM Index'] if not pd.isna(row['BOM Index']) else \"\"\n",
" bom_indices.append(bom_index)\n",
"\n",
"# Step 4: Write comments and BOM Index to their respective columns in sheet_summary\n",
"# Write the headers first\n",
"sheet_summary.cell(row=1, column=comment_start_column_index, value=comment_header)\n",
"sheet_summary.cell(row=1, column=bom_index_column_index, value=bom_index_header)\n",
"\n",
"# Write comments and BOM Index for each row\n",
"for idx, (comment, bom_index) in enumerate(zip(comments, bom_indices)):\n",
" comment_cell = sheet_summary.cell(row=idx + 2, column=comment_start_column_index)\n",
" bom_index_cell = sheet_summary.cell(row=idx + 2, column=bom_index_column_index)\n",
" \n",
" comment_cell.value = comment.strip() # Remove trailing whitespace\n",
" bom_index_cell.value = bom_index\n",
" \n",
" # Align the text in the comment and BOM Index cells to center\n",
" comment_cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)\n",
" bom_index_cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)\n",
"\n",
" # Set border for the comment and BOM Index cells\n",
" comment_cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" bom_index_cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"# Step 5: Set column widths and text alignment for 'Comment' and 'BOM Index' columns\n",
"column_Q_width = 40\n",
"column_R_width = 20 # Set this to a width appropriate for BOM Index\n",
"for row in sheet_summary.iter_rows(min_row=1, max_row=max_row_CTB, min_col=comment_start_column_index, max_col=bom_index_column_index):\n",
" for cell in row:\n",
" # Set alignment to center\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" # Set width for column Q ('Comment')\n",
" if cell.column_letter == sheet_summary.cell(row=1, column=comment_start_column_index).column_letter:\n",
" sheet_summary.column_dimensions[cell.column_letter].width = column_Q_width\n",
" # Set width for column R ('BOM Index')\n",
" elif cell.column_letter == sheet_summary.cell(row=1, column=bom_index_column_index).column_letter:\n",
" sheet_summary.column_dimensions[cell.column_letter].width = column_R_width\n",
"\n",
"# Display or further process df_summary_with_top_level\n",
"#display(df_summary_with_top_level)\n",
"\n",
"######################################################################################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_summary[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row of the sheet_summary worksheet\n",
"sheet_summary.auto_filter.ref = sheet_summary.dimensions\n",
"\n",
"######################################################################################\n",
"#***********************************************************************************\n",
"# Formatting SNAPSHOT \n",
"#***********************************************************************************\n",
"####################################################################################\n",
"# Define sheet_snapshot\n",
"sheet_snapshot = workbook['Snapshot']\n",
"max_row_snapshot = sheet_snapshot.max_row\n",
"\n",
"###################################\n",
"###Formatting tab 1 Snapshot \n",
"##################################\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_snapshot[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Add filters to the first row of the sheet_snapshot worksheet\n",
"sheet_snapshot.auto_filter.ref = sheet_snapshot.dimensions\n",
"\n",
"# Set column widths and text alignment for columns A to AI)\n",
"for column in sheet_snapshot.iter_cols(min_col=1, max_col=35):\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['B', 'C', 'D', 'E', 'K', 'L', 'V', 'W', 'X', 'Y', 'Z', 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI']:\n",
" sheet_snapshot.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['A','I','J', 'M']:\n",
" sheet_snapshot.column_dimensions[column_letter].width = 32\n",
" else:\n",
" sheet_snapshot.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in the current column\n",
" for cell in column:\n",
" if cell.row == 1:\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
"\n",
"###################################################################\n",
"### Apply conditional formatting for |Top-Level Status| column\n",
"###################################################################\n",
"fill_green = PatternFill(start_color='C6EFCE', end_color='C6EFCE', fill_type='solid') # Green fill\n",
"fill_red = PatternFill(start_color='FFC7CE', end_color='FFC7CE', fill_type='solid') # Red fill\n",
"fill_dark_green = PatternFill(start_color='6FAC46', end_color='6FAC46', fill_type='solid') # Dark green fill\n",
"fill_orange = PatternFill(start_color='ED7D31', end_color='ED7D31', fill_type='solid') # orange fill\n",
"\n",
"for row in range(2, max_row_CTB + 1):\n",
" cell = sheet_snapshot.cell(row=row, column=1) # Assuming 'Top-Level Status' is in column 1 (A)\n",
" if cell.value == 'Clear-to-Build':\n",
" cell.fill = fill_green\n",
" if cell.value == 'Completed - No Backlog':\n",
" cell.fill = fill_dark_green\n",
" if cell.value == 'Not completed - No Backlog':\n",
" cell.fill = fill_orange\n",
" elif cell.value == 'Short':\n",
" cell.fill = fill_red\n",
" \n",
"###################################################################\n",
"### Apply conditional formatting for |Production Status| = Industrialized or contain WIP \n",
"###################################################################\n",
"# Define the fill for the conditional formatting\n",
"green_fill = PatternFill(start_color='D8E4BC', end_color='D8E4BC', fill_type='solid')\n",
"blue_fill = PatternFill(start_color='DAEEF3', end_color='DAEEF3', fill_type='solid')\n",
"gray_fill = PatternFill(start_color='F2F2F2', end_color='DAEEF3', fill_type='solid')\n",
"\n",
"# Define the font color\n",
"font_color = Font(color='000000') # Black font color\n",
"\n",
"# Iterate over each row in column J\n",
"for row in range(2, max_row_snapshot + 1): # Start from row 2 since row 1 contains headers\n",
" # Get the value in the current cell in column J\n",
" value_in_J = sheet_snapshot.cell(row=row, column=10).value # Column J is the 9th column\n",
" \n",
" # Check if the value in column J is 'Industrialized'\n",
" if value_in_J == 'Industrialized':\n",
" # Apply the green fill to the entire row and set the font color\n",
" for col in range(2, 22): # Assuming 22 is the last column index\n",
" cell = sheet_snapshot.cell(row=row, column=col)\n",
" cell.fill = green_fill\n",
" cell.font = font_color\n",
" # Check if the value in column J contains 'WIP'\n",
" elif 'WIP' in str(value_in_J):\n",
" # Apply the blue fill to the entire row and set the font color\n",
" for col in range(2, 22): # Assuming 22 is the last column index\n",
" cell = sheet_snapshot.cell(row=row, column=col)\n",
" cell.fill = blue_fill\n",
" cell.font = font_color\n",
"\n",
" # Check if the value in column J contains 'Completed'\n",
" elif 'Completed' in str(value_in_J):\n",
" # Apply the blue fill to the entire row and set the font color\n",
" for col in range(2, 22): # Assuming 22 is the last column index\n",
" cell = sheet_snapshot.cell(row=row, column=col)\n",
" cell.fill = gray_fill\n",
" cell.font = font_color\n",
"\n",
"##########################\n",
"#### Applied thick border to |Top-Level Status|Pty Indice| ... |Production Status|\n",
"###########################\n",
"# Define thick border style for left and right sides\n",
"thick_side = Side(style='thick')\n",
"\n",
"# Define thin black border style for top and bottom sides\n",
"thin_black_side = Side(style='thin', color='000000')\n",
"\n",
"# Define the column indices for the range\n",
"column_indices = [1, 2, 8, 10] # Columns A, B, H, and J\n",
"\n",
"# Iterate over each row and apply the defined border style to the specified columns\n",
"for row in range(2, max_row_snapshot + 1): # Assuming max_row is already defined\n",
" for col_index in column_indices:\n",
" cell = sheet_snapshot.cell(row=row, column=col_index)\n",
" cell.border = Border(\n",
" left=thick_side,\n",
" right=thick_side,\n",
" top=thin_black_side,\n",
" bottom=thin_black_side\n",
" )\n",
" \n",
"###########################\n",
"#### |Pty Indice|..|Qty Clear to Build| ..|IDD Top Level| in bold\n",
"###########################\n",
"# Define font colors\n",
"green_font_color = '24CA77' # Green font color\n",
"red_font_color = 'C00000' # Red font color\n",
"\n",
"# Define font styles\n",
"bold_font = Font(bold=True)\n",
"green_font = Font(color=green_font_color, bold=True) # Bold font with green color\n",
"red_font = Font(color=red_font_color, bold=True) # Bold font with red color\n",
"\n",
"# Apply bold font to 'Pty Indice' (Column B), 'IDD Top Level' (Column D), and 'Qty Clear to Build' (Column H)\n",
"columns_to_bold = [2, 4, 8] # Column B is 2, Column D is 4, Column H is 8\n",
"\n",
"for col_index in columns_to_bold:\n",
" for row in range(2, sheet_snapshot.max_row + 1): # Start from row 2 to skip the header\n",
" cell = sheet_snapshot.cell(row=row, column=col_index)\n",
" cell.font = bold_font\n",
"\n",
"# Apply font color based on 'Top-Level Status' to 'Pty Indice' (Column B) and 'Qty Clear to Build' (Column H)\n",
"for row in range(2, sheet_snapshot.max_row + 1):\n",
" top_level_status_cell = sheet_snapshot.cell(row=row, column=1) # Assuming 'Top-Level Status' is in Column A\n",
" pty_indice_cell = sheet_snapshot.cell(row=row, column=2) # Column B for 'Pty Indice'\n",
" qty_clear_to_build_cell = sheet_snapshot.cell(row=row, column=8) # Column H for 'Qty Clear to Build'\n",
" top_level_status = top_level_status_cell.value\n",
"\n",
" # Apply font color based on 'Top-Level Status'\n",
" if top_level_status == 'Clear-to-Build':\n",
" pty_indice_cell.font = green_font\n",
" qty_clear_to_build_cell.font = green_font\n",
" elif top_level_status == 'Short':\n",
" pty_indice_cell.font = red_font\n",
" qty_clear_to_build_cell.font = red_font\n",
" else:\n",
" # Reset font color to default (black) if neither condition is met\n",
" pty_indice_cell.font = Font(bold=True)\n",
" qty_clear_to_build_cell.font = Font(bold=True)\n",
"\n",
"####################################################################\n",
"# Highlight discripency between |IDD Backlog| and |Remain. Crt. Qty.|\n",
"#####################################################################\n",
"# Define the fill for highlighting and font color\n",
"highlight_fill = PatternFill(start_color=\"FFFFCC\", end_color=\"FFFFCC\", fill_type=\"solid\") # Light yellow background\n",
"highlight_font = Font(color=\"C00000\") # Dark red text\n",
"\n",
"# Get the column index for 'IDD Backlog Qty' and 'Remain. crit. Qty'\n",
"idd_backlog_col_idx = df_snapshot.columns.get_loc('IDD Backlog Qty') + 1 # Adding 1 because openpyxl is 1-indexed\n",
"remain_crit_qty_col_idx = df_snapshot.columns.get_loc('Remain. crit. Qty') + 1 # Adding 1 because openpyxl is 1-indexed\n",
"\n",
"# Apply conditional formatting\n",
"for row in range(2, len(df_snapshot) + 2): # Starting from row 2 to skip header\n",
" idd_backlog_cell = sheet_snapshot.cell(row=row, column=idd_backlog_col_idx)\n",
" remain_crit_qty_cell = sheet_snapshot.cell(row=row, column=remain_crit_qty_col_idx)\n",
" \n",
" if idd_backlog_cell.value != remain_crit_qty_cell.value:\n",
" idd_backlog_cell.fill = highlight_fill\n",
" idd_backlog_cell.font = highlight_font\n",
"\n",
"##############################\n",
"# Define the currency format & date format \n",
"###############################\n",
"currency_format = '$#,##0.00'\n",
"\n",
"# Apply the currency format to the specific columns (N and O)\n",
"for row in range(2, max_row_snapshot + 1):\n",
" # Column N: IDD Marge Standard (unit)\n",
" cell = sheet_snapshot.cell(row=row, column=14) # Column N is the 14th column\n",
" cell.number_format = currency_format\n",
" \n",
" # Column O: IDD Sale Price\n",
" cell = sheet_snapshot.cell(row=row, column=15) # Column O is the 15th column\n",
" cell.number_format = currency_format\n",
"\n",
" # Column Q: IDD Production Cost (unit)\n",
" cell = sheet_snapshot.cell(row=row, column=16) # Column Q is the 16th column\n",
" cell.number_format = currency_format\n",
"\n",
"\n",
"#########################################################################################################################\n",
"# 09/23 - Apply color formating for [W] to [AA] in #DDEBF7, [AB] to [AE] in #FFF2CC and [AF] to [AH] in #E2EFDA\n",
"########################################################################################################################\n",
"# Define the fills for the different column ranges\n",
"fill_w_ab = PatternFill(start_color='DDEBF7', end_color='DDEBF7', fill_type='solid') # Light blue fill\n",
"fill_ac_af = PatternFill(start_color='FFF2CC', end_color='FFF2CC', fill_type='solid') # Light yellow fill\n",
"fill_ag_ai = PatternFill(start_color='E2EFDA', end_color='E2EFDA', fill_type='solid') # Light green fill\n",
"\n",
"# Define the column indices for each range\n",
"range_w_ab = range(23, 29)\n",
"range_ac_af = range(29, 33) \n",
"range_ag_ai = range(33, 36) \n",
"\n",
"# Apply the color formatting for each row and each range\n",
"for row in range(2, max_row_snapshot + 1): # Assuming max_row_snapshot is the maximum row number\n",
" # Apply fill for columns W to AB\n",
" for col in range_w_ab:\n",
" cell = sheet_snapshot.cell(row=row, column=col)\n",
" cell.fill = fill_w_ab\n",
" \n",
" # Apply fill for columns AC to AF\n",
" for col in range_ac_af:\n",
" cell = sheet_snapshot.cell(row=row, column=col)\n",
" cell.fill = fill_ac_af\n",
"\n",
" # Apply fill for columns AG to AI\n",
" for col in range_ag_ai:\n",
" cell = sheet_snapshot.cell(row=row, column=col)\n",
" cell.fill = fill_ag_ai\n",
"\n",
"###################################################################\n",
"### Save the changes to the Excel file \n",
"###################################################################\n",
"workbook.save(original_input)\n",
"\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"\n",
"# WIP 10/08\n",
"#***************************************************************************************************************************\n",
"############################################################################################################################\n",
"## ############ ########### ############# #### ## \n",
"## ## ## ## ## ## ## ## ##\n",
"## ## ## ## ## ## ## ## ##\n",
"## ############# # ## ## ## ## ##\n",
"## ## ## ## ## ## ## ## ## \n",
"## ## ## ## ## ## ## # ##\n",
"## ## ## ########### ############# ## ####\n",
"############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"#***************************************************************************************************************************\n",
"Path_ADCN = 'Inputs\\IDD_ENG-Changes'\n",
"\n",
"# Define date and path\n",
"input_ADCNReport_formatted = os.path.join(Path_ADCN, f'CM_IDD_ADCN-Report_Formatted.xlsx') \n",
"\n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the existing output workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(\"Output workbook loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Output workbook not found: {e}\")\n",
" exit()\n",
"\n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |CM-ADCNReport| ...')\n",
"\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_ADCNReport = pd.read_excel(input_ADCNReport_formatted, sheet_name=0)\n",
" #print(\"Pending Report files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"Input ADCN Report file not found: {e}\")\n",
" exit()\n",
"\n",
"\n",
"# Convert columns to datetime\n",
"''' Replaced 10/15 to avoid warning\n",
"df_ADCNReport['Created'] = pd.to_datetime(df_ADCNReport['Created'], errors='coerce')\n",
"df_ADCNReport['Release Date'] = pd.to_datetime(df_ADCNReport['Release Date'], errors='coerce')\n",
"'''\n",
"# Assuming dates are in 'YYYY-MM-DD' format. Modify the format string if needed.\n",
"df_ADCNReport['Created'] = pd.to_datetime(df_ADCNReport['Created'], format='%Y-%m-%d', errors='coerce')\n",
"df_ADCNReport['Release Date'] = pd.to_datetime(df_ADCNReport['Release Date'], format='%Y-%m-%d', errors='coerce')\n",
"\n",
"####################################################################################################################\n",
"########################################## Creating |CM-LaborReport| ####################\n",
"###################################################################################################################\n",
"# Check if \"CM-TurnoverReport\" sheet already exists and delete it if it does\n",
"if 'CM-ADCNReport' in workbook.sheetnames:\n",
" del workbook['CM-ADCNReport']\n",
"\n",
"# Create new \"CM-ADCNReport\" sheet\n",
"sheet_ADCNReport = workbook.create_sheet(title='CM-ADCNReport')\n",
"\n",
"# Write headers to Excel\n",
"for c_idx, header in enumerate(df_ADCNReport.columns, start=1):\n",
" sheet_ADCNReport.cell(row=1, column=c_idx, value=header)\n",
"\n",
"# Identify the columns for 'Created' and 'Release Date' (D and E correspond to 4 and 5 respectively)\n",
"created_col = 4\n",
"release_date_col = 5\n",
"\n",
"# Write data to Excel and apply date formatting\n",
"for r_idx, row in enumerate(df_ADCNReport.values, start=2):\n",
" for c_idx, value in enumerate(row, start=1):\n",
" cell = sheet_ADCNReport.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
" # Apply short date format to 'Created' and 'Release Date' columns\n",
" if c_idx in [created_col, release_date_col]:\n",
" # Use pd.Timestamp check for pandas datetime\n",
" if isinstance(value, pd.Timestamp) or isinstance(value, datetime):\n",
" cell.number_format = 'MM/DD/YYYY' # Short date format\n",
"\n",
"###############################################################################################################\n",
"################################################ Formatting |CM-ADCNReport| #################################\n",
"###############################################################################################################\n",
"# Apply autofilter on headers\n",
"sheet_ADCNReport.auto_filter.ref = sheet_ADCNReport.dimensions\n",
"\n",
"# Create a light green fill\n",
"light_green_fill = PatternFill(start_color=\"E2EFDA\", end_color=\"E2EFDA\", fill_type=\"solid\")\n",
"\n",
"# Apply the light green fill to row 2 of column M (cell M2)\n",
"sheet_ADCNReport.cell(row=2, column=19).fill = light_green_fill\n",
"\n",
"# Apply formatting to the header row\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"header_alignment = Alignment(horizontal='left', vertical='center')\n",
"header_border = Border(\n",
" top=Side(style='medium'),\n",
" bottom=Side(style='medium'),\n",
" left=Side(style='medium'),\n",
" right=Side(style='medium')\n",
")\n",
"\n",
"for cell in sheet_ADCNReport[1]: # Header row\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = header_alignment\n",
" cell.border = header_border\n",
"\n",
"# Set column widths and text alignment for relevant columns\n",
"for column in sheet_ADCNReport.iter_cols(min_col=1, max_col=18):\n",
" for cell in column:\n",
" if cell.row == 1: # Header row\n",
" cell.alignment = Alignment(horizontal='left', vertical='center')\n",
" else:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" cell.border = Border(top=Side(style='thin'), right=Side(style='thin'), bottom=Side(style='thin'), left=Side(style='thin'))\n",
" \n",
" # Adjust column widths\n",
" column_letter = column[0].column_letter\n",
" if column_letter in ['E', 'F', 'G']:\n",
" sheet_ADCNReport.column_dimensions[column_letter].width = 15\n",
" elif column_letter in ['A', 'B', 'D']:\n",
" sheet_ADCNReport.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['J', 'L', 'M', 'K']:\n",
" sheet_ADCNReport.column_dimensions[column_letter].width = 40\n",
" elif column_letter in ['N', 'C']:\n",
" sheet_ADCNReport.column_dimensions[column_letter].width = 25\n",
" elif column_letter in ['Q', 'R']:\n",
" sheet_ADCNReport.column_dimensions[column_letter].width = 60\n",
" else:\n",
" sheet_ADCNReport.column_dimensions[column_letter].width = 10\n",
"\n",
" # Set the width of column S to 15\n",
"sheet_ADCNReport.column_dimensions['S'].width = 15 # Set width of column Q to 15\n",
"\n",
"# Set column J (10) and Q (17) to left alignment\n",
"j_column_index = 10 # Column J (1-indexed)\n",
"q_column_index = 17 # Column Q (1-indexed)\n",
"\n",
"# Align column J to the left\n",
"for cell in sheet_ADCNReport.iter_rows(min_row=2, min_col=j_column_index, max_col=j_column_index):\n",
" for c in cell:\n",
" c.alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Align column Q to the left\n",
"for cell in sheet_ADCNReport.iter_rows(min_row=2, min_col=q_column_index, max_col=q_column_index):\n",
" for c in cell:\n",
" c.alignment = Alignment(horizontal='left', vertical='center')\n",
"\n",
"# Set column K (11) and R (18) to right alignment\n",
"k_column_index = 11 # Column K (1-indexed)\n",
"r_column_index = 18 # Column R (1-indexed)\n",
"\n",
"# Align column K to the right\n",
"for cell in sheet_ADCNReport.iter_rows(min_row=2, min_col=k_column_index, max_col=k_column_index):\n",
" for c in cell:\n",
" c.alignment = Alignment(horizontal='right', vertical='center')\n",
"\n",
"# Align column R to the right\n",
"for cell in sheet_ADCNReport.iter_rows(min_row=2, min_col=r_column_index, max_col=r_column_index):\n",
" for c in cell:\n",
" c.alignment = Alignment(horizontal='right', vertical='center')\n",
"\n",
"################################################################################\n",
"# Color formating \n",
"#################################################################################\n",
"# Define the fill colors\n",
"orange_fill = PatternFill(start_color=\"FFE6CD\", end_color=\"FFE6CD\", fill_type=\"solid\") # Orange color fill\n",
"blue_fill = PatternFill(start_color=\"DCE6F1\", end_color=\"DCE6F1\", fill_type=\"solid\") # Blue color fill\n",
"red_font = Font(color=\"FF0000\") # Red font color for 'Not released'\n",
"green_bold_font = Font(color=\"008000\", bold=True) # Green and bold font color for values that are not 'Not released'\n",
"yellow_fill = PatternFill(start_color=\"FFF2BD\", end_color=\"FFF2BD\", fill_type=\"solid\") # For inserted row that does not have a ADCN#\n",
"red_fill = PatternFill(start_color=\"F2DCDB\", end_color=\"F2DCDB\", fill_type=\"solid\") # Red color fill\n",
"\n",
"# Iterate over rows to color columns B to G based on 'Release Date' and 'ADCN#'\n",
"for row in range(2, sheet_ADCNReport.max_row + 1): # Start from row 2 to skip header\n",
" release_date_cell = sheet_ADCNReport.cell(row=row, column=5) # Column E for 'Release Date'\n",
" adcn_cell = sheet_ADCNReport.cell(row=row, column=2) # Column B for 'ADCN#'\n",
" esr_cell = sheet_ADCNReport.cell(row=row, column=3) # Column C for 'ESR#'\n",
" \n",
"# Check if 'ESR#' is 'ESR to be submitted'\n",
" if esr_cell.value == 'ESR to be submitted':\n",
" # Apply red fill to columns B to G\n",
" for col in range(2, 8): # Columns B to G (2 to 7)\n",
" sheet_ADCNReport.cell(row=row, column=col).fill = red_fill\n",
" \n",
" # Set font color to red for 'ESR#' in Column C\n",
" esr_cell.font = red_font # Apply red font to 'ESR#'\n",
" \n",
" continue # Skip to the next row after processing\n",
"\n",
" # Check if 'ADCN#' is 'ADCN not created'\n",
" if adcn_cell.value == 'ADCN not created':\n",
" # Apply yellow fill and red font to columns B to G\n",
" for col in range(2, 8): # Columns B to G (2 to 7)\n",
" sheet_ADCNReport.cell(row=row, column=col).fill = yellow_fill # Apply yellow fill\n",
" # Apply red font to Column B\n",
" #adcn_cell.font = red_font\n",
" continue # Skip to the next row after processing\n",
" \n",
" # Now check the 'Release Date'\n",
" if release_date_cell.value == 'Not released': # If 'Not released'\n",
" fill_color = orange_fill # Orange color\n",
" release_date_cell.font = red_font # Set font to red\n",
" elif release_date_cell.value is not None and release_date_cell.value != \"\": # If it contains other values\n",
" fill_color = blue_fill # Blue color\n",
" release_date_cell.font = green_bold_font # Set font to green\n",
" else:\n",
" continue # Skip if it is empty\n",
"\n",
" # Apply fill color to columns B to G\n",
" for col in range(2, 8): # Columns B to G (2 to 7)\n",
" sheet_ADCNReport.cell(row=row, column=col).fill = fill_color\n",
"\n",
"# Gray font #A6A6A6 for [H] to [N]\n",
"gray_font = Font(color=\"A6A6A6\") # Gray font color for columns H to L\n",
"dark_gray_font = Font(color=\"808080\")\n",
"\n",
"# Gray font for columns H to L\n",
"for row in range(2, sheet_ADCNReport.max_row + 1): # Start from row 2 to skip header\n",
" for col in range(8, 15): # Columns H to N (8 to 12)\n",
" sheet_ADCNReport.cell(row=row, column=col).font = gray_font\n",
"\n",
"# Apply dark gray font to Column A\n",
"for row in range(2, sheet_ADCNReport.max_row + 1): # Start from row 2 to skip header\n",
" sheet_ADCNReport.cell(row=row, column=1).font = dark_gray_font # Column A is 1\n",
"\n",
"# Apply bold font to Column B \n",
"bold_font = Font(bold=True) # Define bold font\n",
"for row in range(2, sheet_ADCNReport.max_row + 1): # Start from row 2 to skip header\n",
" cell_b = sheet_ADCNReport.cell(row=row, column=2) # Column B\n",
" cell_b.font = bold_font # Apply bold font\n",
" # If the ADCN is 'ADCN not created', set the font color to red again\n",
" if cell_b.value == 'ADCN not created':\n",
" cell_b.font = red_font # Reapply red font to ensure it is kept\n",
"\n",
"#########################################################\n",
"# Color formating on 'Hot' [O] and 'Holding Prod' [P]\n",
"#########################################################\n",
"# If column M not empty --> Hot fill: #FFC7CE fill and #9C0006 font\n",
"# If column N not empty --> Holding fill: #FFCC99 fill and #3F3F76 font\n",
"hot_fill = PatternFill(start_color=\"FFC7CE\", end_color=\"FFC7CE\", fill_type=\"solid\") # Hot fill color\n",
"hot_font = Font(color=\"9C0006\") # Font color for 'Hot'\n",
"\n",
"holding_fill = PatternFill(start_color=\"FFCC99\", end_color=\"FFCC99\", fill_type=\"solid\") # Holding fill color\n",
"holding_font = Font(color=\"3F3F76\") # Font color for 'Holding Prod'\n",
"\n",
"# Iterate over rows for 'Hot' and 'Holding Prod' color formatting\n",
"for row in range(2, sheet_ADCNReport.max_row + 1): # Start from row 2 to skip header\n",
" hot_cell = sheet_ADCNReport.cell(row=row, column=15) # Column O for 'Hot'\n",
" holding_cell = sheet_ADCNReport.cell(row=row, column=16) # Column P for 'Holding Prod'\n",
" \n",
" # Check 'Hot' column\n",
" if hot_cell.value not in [None, \"\"]: # If not empty\n",
" hot_cell.fill = hot_fill # Apply fill\n",
" hot_cell.font = hot_font # Apply font color\n",
" \n",
" # Check 'Holding Prod' column\n",
" if holding_cell.value not in [None, \"\"]: # If not empty\n",
" holding_cell.fill = holding_fill # Apply fill\n",
" holding_cell.font = holding_font # Apply font color\n",
"\n",
"#######################################################################################################\n",
"# Hide column J and K as they are useless until finding a way to get the drawings number of all subs \n",
"#######################################################################################################\n",
"columns_to_hide = ['L', 'M']\n",
"for col in columns_to_hide:\n",
" sheet_ADCNReport.column_dimensions[col].hidden = True\n",
"\n",
"###################################################################\n",
"### Save the changes to the Excel file \n",
"###################################################################\n",
"workbook.save(original_input)\n",
"\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"# New 10/29\n",
"#***************************************************************************************************************************\n",
"############################################################################################################################\n",
"## ########## ############# ########## ############ ##########\n",
"## ## ## ## ## ## ## ##\n",
"## ## ## ## ## ## ## ## \n",
"## ########## ######### ## ## ############ #### ##########\n",
"## ## ## ## ## ## ## ##\n",
"## ## ## ## ## ## ## ##\n",
"## ########## ############ ########## ## ## ########## ORTAGES\n",
"############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"#### Load the workbook\n",
"workbook = load_workbook(original_input)\n",
"df_summary = pd.read_excel(original_input, sheet_name='Summary')\n",
"\n",
"#### Copy existing tab |Summary| with all the rows 'Supplier' = 'SEDA' & 'Blank' \n",
"# Filter rows for 'SEDA' and blank Suppliers\n",
"df_seda_shortages = df_summary[(df_summary['Supplier'] == 'SAFRAN ELEC & DEFENSE(S9412)') | (df_summary['Supplier'].isnull())].copy()\n",
"\n",
"#### Insert column 'SEDA Component' based on tab |Magic-Decoder| from input file CM-Priority \n",
"# Load 'Magic-Decoder' sheet from 'CM-Priority' workbook\n",
"try:\n",
" df_magic_decoder = pd.read_excel(os.path.join(Path, 'CM_Priority_Database.xlsx'), sheet_name='Magic-Decoder')\n",
" \n",
" # Fill NaN values in 'SEDA Component' with '--' and assign back to the column\n",
" df_magic_decoder['SEDA Component'] = df_magic_decoder['SEDA Component'].fillna('--')\n",
"\n",
" # Merge to add 'SEDA Component' based on 'IDD Component'\n",
" df_seda_shortages = df_seda_shortages.merge(df_magic_decoder[['IDD Component', 'SEDA Component']],\n",
" on='IDD Component', how='left')\n",
"\n",
" # Check if 'SEDA Component' was successfully merged\n",
" if 'SEDA Component' in df_seda_shortages.columns:\n",
" # Get the index of 'IDD Component'\n",
" idd_index = df_seda_shortages.columns.get_loc('IDD Component') + 1\n",
" \n",
" # Use insert to place 'SEDA Component' right after 'IDD Component'\n",
" df_seda_shortages.insert(idd_index, 'SEDA Component', df_seda_shortages.pop('SEDA Component'))\n",
"\n",
"except FileNotFoundError:\n",
" print(\"Error: 'CM_Priority_Database.xlsx' file or 'Magic-Decoder' tab not found.\")\n",
" exit()\n",
"\n",
"# Delete row where 'Remain. crit. Qty' = '0' \n",
"df_seda_shortages = df_seda_shortages[df_seda_shortages['Remain. crit. Qty'] != 0]\n",
"\n",
"# Remove duplicate rows based on specific columns (e.g., 'IDD Component' and 'SEDA Component')\n",
"df_seda_shortages = df_seda_shortages.drop_duplicates(subset=['Pty Indice', 'IDD Component', 'SEDA Component', 'Level'])\n",
"\n",
"# New code to delete useless rows:\n",
"# Identify rows with 'Level' = 0 followed by another row with 'Level' = 0\n",
"rows_to_delete = df_seda_shortages[(df_seda_shortages['Level'] == 0) & (df_seda_shortages['Level'].shift(-1) == 0)].index\n",
"\n",
"# Drop identified rows if any exist\n",
"if not rows_to_delete.empty:\n",
" df_seda_shortages = df_seda_shortages.drop(rows_to_delete).reset_index(drop=True)\n",
"\n",
"\n",
"###########################################\n",
"# Create |SEDA-Shortages|\n",
"###########################################\n",
"# Remove the sheet if it already exists, and then create it again at index 2 (third position)\n",
"if 'SEDA-Shortages' in workbook.sheetnames:\n",
" workbook.remove(workbook['SEDA-Shortages'])\n",
"seda_sheet = workbook.create_sheet(title='SEDA-Shortages', index=2)\n",
"\n",
"# Write headers\n",
"for col_num, column_title in enumerate(df_seda_shortages.columns, start=1):\n",
" seda_sheet.cell(row=1, column=col_num, value=column_title)\n",
"\n",
"# Write data rows\n",
"for row_num, row_data in enumerate(df_seda_shortages.values, start=2):\n",
" for col_num, cell_value in enumerate(row_data, start=1):\n",
" seda_sheet.cell(row=row_num, column=col_num, value=cell_value)\n",
"\n",
"# Write the date 'file_date_inventory' in cell Q2\n",
"seda_sheet.cell(row=2, column=17, value=file_date_inventory) # Q is the 17th column\n",
"\n",
"'''\n",
"# Define column indexes based on positions of 'SEDA Component' and 'Level'\n",
"seda_component_col = df_seda_shortages.columns.get_loc('SEDA Component') + 1 # +1 for 1-based indexing in Excel\n",
"level_col = df_seda_shortages.columns.get_loc('Level') + 1\n",
"\n",
"# Iterate through rows to check for empty 'SEDA Component' values where 'Level' is not 0\n",
"for row in range(2, seda_sheet.max_row + 1): # Start from row 2 to skip headers\n",
" level_value = seda_sheet.cell(row=row, column=level_col).value\n",
" seda_component_value = seda_sheet.cell(row=row, column=seda_component_col).value\n",
" \n",
" # Check if 'Level' is not 0 and 'SEDA Component' is empty\n",
" if level_value != 0 and (seda_component_value is None or seda_component_value == \"\"):\n",
" seda_sheet.cell(row=row, column=seda_component_col, value=\"--\")\n",
"''' \n",
"###########################################\n",
"# Formating - Same as exixting |Summary|\n",
"###########################################\n",
"# Apply autofilter on headers\n",
"seda_sheet.auto_filter.ref = seda_sheet.dimensions\n",
"\n",
"# Apply the light green fill to row 2 of column Q (cell Q2)\n",
"seda_sheet.cell(row=2, column=17).fill = light_green_fill\n",
"\n",
"# Constants for column widths\n",
"column_P_width = 15 # Width for Column P\n",
"column_R_width = 40 # Width for 'Comment' column (R)\n",
"column_S_width = 20 # Width for 'BOM Index' column (S)\n",
"\n",
"# Define column index numbers for clarity\n",
"comment_column_index = 18 # R\n",
"bom_index_column_index = 19 # S\n",
"\n",
"# Define styles\n",
"font_color_black = '000000'\n",
"#thin_side = Side(style='thin') # Define thin side\n",
"alignment = Alignment(horizontal=\"center\", vertical=\"center\")\n",
"header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"thin_border = Border(left=Side(style=\"thin\"), \n",
" right=Side(style=\"thin\"), \n",
" top=Side(style=\"thin\"), \n",
" bottom=Side(style=\"thin\"))\n",
"\n",
"# Apply level-based conditional formatting to the 'Level' column (G)\n",
"min_row, max_row_CTB = 2, seda_sheet.max_row # Adjust min_row if headers start lower\n",
"col_G = 7 # Column G is the 7th column\n",
"for row in range(min_row, max_row_CTB + 1):\n",
" cell_value = seda_sheet.cell(row=row, column=col_G).value\n",
" if cell_value is not None:\n",
" # Set fill colors based on level\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" else:\n",
" fill_color = None # No color if the level is outside the range\n",
"\n",
" # Apply the fill and font to the cell if fill_color is not None\n",
" if fill_color:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type=\"solid\")\n",
" seda_sheet.cell(row=row, column=col_G).fill = fill\n",
" seda_sheet.cell(row=row, column=col_G).font = Font(color=font_color_black, bold=False)\n",
"\n",
"# Set column widths and text alignment for columns A to P\n",
"for column in seda_sheet.iter_cols(min_col=1, max_col=16):\n",
" column_letter = column[0].column_letter # Get the column letter for width adjustment\n",
"\n",
" # Set widths based on column letter\n",
" if column_letter in ['A', 'C', 'D', 'E', 'F', 'J', 'K', 'L', 'M', 'N', 'O']:\n",
" seda_sheet.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['B', 'I', 'P']:\n",
" seda_sheet.column_dimensions[column_letter].width = 30\n",
" else:\n",
" seda_sheet.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in each column\n",
" for cell in column:\n",
" # Set alignment based on row number\n",
" cell.alignment = Alignment(horizontal='left' if cell.row == 1 else 'center', vertical='center')\n",
" cell.border = thin_border # Apply border\n",
"\n",
"# Set alignment specifically for columns P, R, and S\n",
"max_row_seda = seda_sheet.max_row # Get max row dynamically\n",
"for row in seda_sheet.iter_rows(min_row=1, max_row=max_row_seda):\n",
" for cell in row:\n",
" # Center alignment for all cells\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
"\n",
" # Right alignment specifically for Column P\n",
" if cell.column == 16: # Column P\n",
" cell.alignment = Alignment(horizontal='right', vertical='center')\n",
" seda_sheet.column_dimensions[cell.column_letter].width = column_P_width # Set width for Column P\n",
"\n",
"# Set width and alignment for 'Comment' and 'BOM Index' columns\n",
"seda_sheet.column_dimensions[get_column_letter(comment_column_index)].width = column_R_width\n",
"seda_sheet.column_dimensions[get_column_letter(bom_index_column_index)].width = column_S_width\n",
"\n",
"# Apply right alignment to column S specifically\n",
"for row in seda_sheet.iter_rows(min_row=1, max_row=max_row_seda):\n",
" for cell in row:\n",
" if cell.column == bom_index_column_index: # Column S\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
"\n",
"# Apply thin border to all cells in columns R and S\n",
"for row in seda_sheet.iter_rows(min_row=1, max_row=max_row_seda):\n",
" # Apply to Column R\n",
" cell_r = row[comment_column_index - 1] # Adjusting index for 0-based\n",
" cell_r.border = thin_border\n",
"\n",
" # Apply to Column S\n",
" cell_s = row[bom_index_column_index - 1] # Adjusting index for 0-based\n",
" cell_s.border = thin_border\n",
" \n",
"# Apply borders to header rows (1 and 2) in column Q\n",
"for row in range(1, 3):\n",
" seda_sheet.cell(row=row, column=17).border = thin_border\n",
"\n",
"# Set width for Column P\n",
"seda_sheet.column_dimensions['P'].width = 30\n",
"\n",
"# Apply header style\n",
"for cell in seda_sheet[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = alignment\n",
" cell.border = thin_border\n",
"\n",
"''' SAVED 10/29\n",
"# Define styles\n",
"header_fill = PatternFill(start_color=\"4472C4\", end_color=\"4472C4\", fill_type=\"solid\")\n",
"header_font = Font(color='FFFFFF', bold=True)\n",
"highlight_fill = PatternFill(start_color=\"FFC7CE\", end_color=\"FFC7CE\", fill_type=\"solid\")\n",
"alignment = Alignment(horizontal=\"center\", vertical=\"center\")\n",
"font_color_black = \"000000\" # Black font color for Level column formatting\n",
"# Define thin border style\n",
"thin_border = Border(left=Side(style=\"thin\"), \n",
" right=Side(style=\"thin\"), \n",
" top=Side(style=\"thin\"), \n",
" bottom=Side(style=\"thin\"))\n",
"\n",
"# Apply level-based conditional formatting to the 'Level' column (G)\n",
"min_row, max_row_CTB = 2, seda_sheet.max_row # Adjust min_row if headers start lower\n",
"col_G = 7 # Column G is the 7th column\n",
"for row in range(min_row, max_row_CTB + 1):\n",
" cell_value = seda_sheet.cell(row=row, column=col_G).value\n",
" if cell_value is not None:\n",
" # Set fill colors based on level\n",
" if cell_value == 0:\n",
" fill_color = '63BE7B' # Green\n",
" elif cell_value == 1:\n",
" fill_color = 'A2C075' # Lighter Green\n",
" elif cell_value == 2:\n",
" fill_color = 'FFEB84' # Yellow\n",
" elif cell_value == 3:\n",
" fill_color = 'FFD166' # Orange\n",
" elif cell_value == 4:\n",
" fill_color = 'F88E5B' # Darker Orange\n",
" elif cell_value == 5:\n",
" fill_color = 'F8696B' # Red\n",
" elif cell_value == 6:\n",
" fill_color = '8B0000' # Darker Red\n",
" else:\n",
" fill_color = None # No color if the level is outside the range\n",
"\n",
" # Apply the fill and font to the cell if fill_color is not None\n",
" if fill_color:\n",
" fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type=\"solid\")\n",
" seda_sheet.cell(row=row, column=col_G).fill = fill\n",
" seda_sheet.cell(row=row, column=col_G).font = Font(color=font_color_black, bold=False)\n",
"\n",
"# Set column widths and text alignment for columns A to P\n",
"for column in seda_sheet.iter_cols(min_col=1, max_col=16):\n",
" column_letter = column[0].column_letter # Get the column letter for width adjustment\n",
"\n",
" # Set widths based on column letter\n",
" if column_letter in ['A', 'C', 'D', 'E', 'F', 'J', 'K', 'L', 'M', 'N', 'O']:\n",
" seda_sheet.column_dimensions[column_letter].width = 20\n",
" elif column_letter in ['B', 'I', 'P']:\n",
" seda_sheet.column_dimensions[column_letter].width = 30\n",
" else:\n",
" seda_sheet.column_dimensions[column_letter].width = 10\n",
"\n",
" # Apply alignment and border to cells in each column\n",
" for cell in column:\n",
" # Set alignment based on row number\n",
" cell.alignment = Alignment(horizontal='left' if cell.row == 1 else 'center', vertical='center')\n",
" cell.border = thin_border # Apply border\n",
"\n",
"max_row_seda = seda_sheet.max_row # Get max row dynamically\n",
"\n",
"# Set alignment and widths for the specified columns\n",
"for row in seda_sheet.iter_rows(min_row=1, max_row=max_row_seda):\n",
" for cell in row:\n",
" # Center alignment for all cells\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
"\n",
" # Right alignment specifically for Column P\n",
" if cell.column == 16: # Column P\n",
" cell.alignment = Alignment(horizontal='right', vertical='center')\n",
" seda_sheet.column_dimensions[cell.column_letter].width = column_P_width # Set width for Column P\n",
"\n",
" # Set width for 'Comment' and 'BOM Index' columns using index numbers\n",
" if cell.column == comment_column_index: # Column R\n",
" seda_sheet.column_dimensions[cell.column_letter].width = column_R_width\n",
" elif cell.column == bom_index_column_index: # Column S\n",
"\n",
"#############################################################################\n",
"#Set column widths and text alignment for 'Comment' and 'BOM Index' columns\n",
"##############################################################################\n",
"# Define widths for the columns\n",
"column_R_width = 40 # Width for 'Comment' column (R)\n",
"column_S_width = 20 # Width for 'BOM Index' column (S)\n",
"\n",
"# Define column index numbers for clarity\n",
"comment_column_index = 18 # R\n",
"bom_index_column_index = 19 # S\n",
"\n",
"# Set alignment and widths for the specified columns\n",
"for row in seda_sheet.iter_rows(min_row=1, max_row=max_row_CTB):\n",
" for cell in row:\n",
" cell.alignment = Alignment(horizontal='center', vertical='center') # Center alignment\n",
"\n",
" # Set width for 'Comment' and 'BOM Index' columns using index numbers\n",
" if cell.column == comment_column_index: # Column R\n",
" seda_sheet.column_dimensions[cell.column_letter].width = column_R_width\n",
" elif cell.column == bom_index_column_index: # Column S\n",
" seda_sheet.column_dimensions[cell.column_letter].width = column_S_width\n",
"\n",
"seda_sheet.column_dimensions[cell.column_letter].width = column_S_width\n",
"\n",
"# Apply borders to header rows (1 and 2) in column Q\n",
"for row in range(1, 3):\n",
" seda_sheet.cell(row=row, column=17).border = thin_border\n",
"\n",
"# Apply header style\n",
"for cell in seda_sheet[1]:\n",
" cell.fill = header_fill\n",
" cell.font = header_font\n",
" cell.alignment = alignment\n",
" cell.border = thin_border\n",
"'''\n",
"\n",
"####################################################\n",
"# Apply conditional formatting on \"Max Qty (GS)\"\n",
"###################################################\n",
"# Red fill for cells with 0\n",
"fill_red = PatternFill(start_color='FFC7CE', end_color='FFC7CE', fill_type='solid') # Red fill\n",
"\n",
"# Define the column index for \"Max Qty (GS)\"\n",
"max_qty_col = df_seda_shortages.columns.get_loc('Max Qty (GS)') + 1\n",
"\n",
"# Define the range for the Max Qty (GS) column\n",
"start_cell = seda_sheet.cell(row=2, column=max_qty_col).coordinate\n",
"end_cell = seda_sheet.cell(row=len(df_seda_shortages) + 1, column=max_qty_col).coordinate\n",
"range_coord = f\"{start_cell}:{end_cell}\"\n",
"\n",
"# Apply conditional formatting only to cells with a numerical value of 0\n",
"seda_sheet.conditional_formatting.add(\n",
" range_coord,\n",
" CellIsRule(operator=\"equal\", formula=['0'], stopIfTrue=True, fill=fill_red)\n",
")\n",
"\n",
"\n",
"##########################################################################\n",
"# Apply conditional formatting for row dedicated to Top-Level (Level = 0)\n",
"##########################################################################\n",
"# Define fill color for Top-Level\n",
"fill_TopLevel = PatternFill(start_color='5B9BD5', end_color='5B9BD5', fill_type='solid') # Blue fill\n",
"\n",
"# Define font colors\n",
"font_color_clear = \"FFFFFF\" # White\n",
"font_color_shortage = \"C00000\" # Dark Red\n",
"font_color_fullyClear = \"24CA77\" # Green\n",
"\n",
"# Define thick border\n",
"thick_border = Border(\n",
" top=Side(style='thick'),\n",
" bottom=Side(style='thick')\n",
")\n",
"\n",
"# Iterate through each row in the sheet\n",
"for row in range(2, max_row_CTB + 1):\n",
" cell_level = seda_sheet.cell(row=row, column=7) # Assuming 'Level' is in column 7 (G)\n",
" cell_max_qty = seda_sheet.cell(row=row, column=15) # Assuming 'Max Qty Top-Level' is in column 15 (N)\n",
" cell_remain_crit_qty = seda_sheet.cell(row=row, column=14) # Assuming 'Remain. crit. Qty' is in column 14 (M)\n",
"\n",
" if cell_level.value == 0:\n",
" # Check if 'Max Qty Top-Level' and 'Remain. crit. Qty' cells are not empty\n",
" if cell_max_qty.value is not None and cell_remain_crit_qty.value is not None:\n",
" # Determine the font color based on the values of 'Max Qty Top-Level' and 'Remain. crit. Qty'\n",
" font_color = font_color_clear # Default font color\n",
" if cell_max_qty.value >= cell_remain_crit_qty.value:\n",
" font_color = font_color_fullyClear\n",
" elif cell_max_qty.value > 0:\n",
" font_color = font_color_clear\n",
" elif cell_max_qty.value == 0:\n",
" font_color = font_color_shortage\n",
"\n",
" # Apply fill color, font color, bold, and border to each cell in the row\n",
" for col in range(1, 16):\n",
" cell = seda_sheet.cell(row=row, column=col)\n",
" cell.fill = fill_TopLevel\n",
" cell.font = Font(color=font_color, bold=True)\n",
" cell.border = thick_border\n",
"\n",
"###################################################################\n",
"### Apply conditional formatting for 'Top-Level Status' column\n",
"###################################################################\n",
"fill_green = PatternFill(start_color='C6EFCE', end_color='C6EFCE', fill_type='solid') # Green fill\n",
"fill_dark_green = PatternFill(start_color='6FAC46', end_color='6FAC46', fill_type='solid') # Dark green fill\n",
"fill_orange = PatternFill(start_color='ED7D31', end_color='ED7D31', fill_type='solid') # orange fill\n",
"\n",
"for row in range(2, max_row_CTB + 1):\n",
" cell = seda_sheet.cell(row=row, column=1) # Assuming 'Top-Level Status' is in column 1 (A)\n",
" if cell.value == 'Clear-to-Build':\n",
" cell.fill = fill_green\n",
" if cell.value == 'Completed - No Backlog':\n",
" cell.fill = fill_dark_green\n",
" if cell.value == 'Not completed - No Backlog':\n",
" cell.fill = fill_orange\n",
" elif cell.value == 'Shortage':\n",
" cell.fill = fill_red\n",
" \n",
"################################################\n",
"# Save the updated workbook\n",
"################################################\n",
"workbook.save(original_input)\n",
"# Close the workbook\n",
"workbook.close()\n",
"\n",
"print(f\"SEDA Shortages added successfully as|SEDA-Shortages| in {original_input}\")\n",
"\n",
"\n",
"#///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"#*********************************************************************************************************************************************\n",
"# FORMATTING |CM-Priority| based of |SNAPSHOT|\n",
"#*********************************************************************************************************************************************\n",
"#####################################################################\n",
"### Formating |Clear to build| from CM-Priority column based on the value after filling it based on the SUMMARY \n",
"#####################################################################\n",
"# if |Clear to build| = 0 --> Red, if |Clear to build| > |Remaining crit. Qty.| --> Green, if |Clear to build| < |Remaining crit. Qty.| --> Black\n",
"\n",
"\n",
"##### ////////////////////////////////// #####\n",
"# Insert section |Dashboard| if needed ##\n",
"##### ////////////////////////////////// #####\n",
"\n",
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ######## ######## ######### ######### ## ## \n",
"## ## ## ## ## ## ## ## ## ## \n",
"## ## #### ######## ######### ######### ######### \n",
"## ## ## ## ### ## ## ## ## ## \n",
"## ######## ## ## ## ## ## ## ## \n",
"##############################################################################################################################\n",
"# --> Copy code if needed <--\n",
"\n",
"\n",
"###################################\n",
"# Function to color a sheet tab\n",
"###################################\n",
"def color_sheet_tab(sheet_name, color):\n",
" if sheet_name in workbook.sheetnames:\n",
" sheet = workbook[sheet_name]\n",
" sheet.sheet_properties.tabColor = color\n",
"\n",
"# Color each tab individually\n",
"#color_sheet_tab('Dashboard', 'D8E4BC') # Green \n",
"color_sheet_tab('Snapshot', 'D8E4BC') # Green\n",
"color_sheet_tab('Summary', 'D8E4BC') # Green\n",
"color_sheet_tab('SEDA-Shortages', 'D8E4BC') # Green\n",
"color_sheet_tab('Gantt', 'B7DEE8') # Bleu turquoise\n",
"color_sheet_tab('Clear-to-Build', 'D8E4BC') # Green\n",
"color_sheet_tab('CM-Inventory', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-BOM', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-Priority', 'B7DEE8') # Bleu turquoise\n",
"color_sheet_tab('CM-Backlog', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-TurnoverReport', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-WIP', 'C5D9F1') # Bleu\n",
"color_sheet_tab('PendingReport', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-Historic', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-LaborReport', 'C5D9F1') # Bleu\n",
"color_sheet_tab('CM-MakeArchitecture', 'E4DFEC') # Purple\n",
"color_sheet_tab('CM-ADCNReport', 'E4DFEC') # Purple\n",
"\n",
"'''\n",
"try:\n",
" # Set 'Snapshot' as the active sheet\n",
" if 'Snapshot' in workbook.sheetnames:\n",
" # Save the workbook with the current filename\n",
" workbook.save(original_input)\n",
" print(\"Workbook saved successfully.\")\n",
"\n",
"except Exception as e:\n",
" print(f\"An unexpected error occurred: {e}\")\n",
"\n",
"finally:\n",
" # Close the workbook with the original_input filename\n",
" workbook.close()\n",
"'''\n",
"\n",
"try:\n",
" # Check if 'Snapshot' is in the sheet names\n",
" if 'Snapshot' in workbook.sheetnames:\n",
" # Save the workbook with the current filename\n",
" workbook.save(original_input)\n",
" print(\"Workbook saved successfully.\")\n",
" else:\n",
" print(\"Sheet 'Snapshot' not found.\")\n",
"\n",
"except Exception as e:\n",
" print(f\"An unexpected error occurred: {e}\")\n",
" \n",
"###################################################################################################################\n",
"# Print the sheet names\n",
"print(\"Sheet names in {}: {}\".format(original_input, workbook.sheetnames))\n",
"print('Transfer Project Overview spreadsheet generated sucessfully!')\n",
"\n",
"# New 08/27\n",
"#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"###################################################################################################################\n",
"#### Update input file CM_Priority_Database.xlsx column ['Shipped'] and ['Remain. crit. Qty']\n",
"###################################################################################################################\n",
"#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"# |Clear-to-Build| and |CM-Inventory| are created before the update of |CM-Priority|, \n",
"# as a consequance, the column ['Shipped'] and ['Remain. crit. Qty'] remain the values from the input df_Priority (CM_Priority_Database.xlsx)\n",
"###################################################################################################################\n",
"# Copy the original file with the updated name\n",
"priority_file_name_updated = os.path.join(Path, 'CM_Priority_Database_updated.xlsx')\n",
"shutil.copy(priority_file_name, priority_file_name_updated)\n",
"\n",
"# Load the original and updated workbooks\n",
"wb_original = openpyxl.load_workbook(priority_file_name)\n",
"wb_updated = openpyxl.load_workbook(priority_file_name_updated)\n",
"\n",
"# Select the active sheet (or specify by name if needed)\n",
"ws_original = wb_original['CM-Priority'] \n",
"ws_updated = wb_updated['CM-Priority'] \n",
"\n",
"# Find the column indexes for 'Pty Indice', 'Shipped', 'Critical Qty', and 'Remain. crit. Qty'\n",
"columns = {}\n",
"for cell in ws_original[1]: # Assuming the first row contains headers\n",
" if cell.value in ['Pty Indice', 'Shipped', 'Critical Qty', 'Remain. crit. Qty']:\n",
" columns[cell.value] = cell.column\n",
"\n",
"# Ensure all necessary columns were found before proceeding\n",
"if not all(col in columns for col in ['Pty Indice', 'Shipped', 'Critical Qty', 'Remain. crit. Qty']):\n",
" raise ValueError(\"Could not find one or more required columns in the header row.\")\n",
"\n",
"# Update 08/29\n",
"# Update the 'Shipped' and calculate 'Remain. crit. Qty' in the updated sheet \n",
"for row in range(2, ws_original.max_row + 1): # Assuming the first row is headers\n",
" # Check if the row exists in df_Priority_updated\n",
" if row - 2 < len(df_Priority_updated):\n",
" pty_indice = df_Priority_updated.loc[row - 2, 'Pty Indice']\n",
" new_shipped = df_Priority_updated.loc[row - 2, 'Shipped']\n",
"\n",
" # Find the corresponding row in the Excel sheet\n",
" for excel_row in ws_updated.iter_rows(min_row=2, max_row=ws_updated.max_row, values_only=False):\n",
" if excel_row[columns['Pty Indice'] - 1].value == pty_indice:\n",
" crit_qty = excel_row[columns['Critical Qty'] - 1].value\n",
" \n",
" # Update the 'Shipped' value regardless of whether it is greater or not\n",
" excel_row[columns['Shipped'] - 1].value = new_shipped\n",
"\n",
" # Calculate 'Remain. crit. Qty'\n",
" remain_crit_qty = max(int(crit_qty) - int(new_shipped), 0)\n",
"\n",
" # Update 'Remain. crit. Qty' in the Excel sheet\n",
" remain_crit_qty_cell = excel_row[columns['Remain. crit. Qty'] - 1]\n",
" remain_crit_qty_cell.value = remain_crit_qty\n",
" remain_crit_qty_cell.number_format = '0' # Format the cell to show integers only\n",
"\n",
" break\n",
" \n",
"# Save the updated workbook\n",
"wb_updated.save(priority_file_name_updated)\n",
"print(f\"File {priority_file_name_updated} created successfully.\")\n",
"\n",
"#New 08/28\n",
"#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"################################################################################################################################################################\n",
"#### Update input file CM_Priority_Database.xlsx column ['Shipped'] and ['Remain. crit. Qty'] with the newlly created file CM_Priority_Database_updated.xlsx \n",
"###############################################################################################################################################################\n",
"#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n",
"# open priority_file_name (CM_Priority_Database.xlsx) and update 'Shipped' and 'Remain. crit. Qty' with the values from priority_file_name_updated (CM_Priority_Database_updated.xlsx)\n",
"\n",
"# Select the active sheets\n",
"#ws_original = wb_original.active # or wb_original['SheetName']\n",
"#ws_updated = wb_updated.active # or wb_updated['SheetName']\n",
"ws_original = wb_original['CM-Priority'] \n",
"ws_updated = wb_updated['CM-Priority'] \n",
"\n",
"# Find the column indexes for 'Pty Indice', 'Shipped', and 'Remain. crit. Qty'\n",
"columns = {}\n",
"for cell in ws_original[1]: # Assuming the first row contains headers\n",
" if cell.value in ['Pty Indice', 'Shipped', 'Critical Qty', 'Remain. crit. Qty']:\n",
" columns[cell.value] = cell.column\n",
"\n",
"# Ensure all necessary columns were found before proceeding\n",
"if not all(col in columns for col in ['Pty Indice', 'Shipped', 'Remain. crit. Qty']):\n",
" raise ValueError(\"Could not find one or more required columns in the header row.\")\n",
"\n",
"# Update the 'Shipped' and 'Remain. crit. Qty' in the original sheet\n",
"for row in range(2, ws_original.max_row + 1): # Assuming the first row is headers\n",
" # Find the corresponding row in the updated Excel sheet\n",
" for excel_row_updated in ws_updated.iter_rows(min_row=2, max_row=ws_updated.max_row, values_only=False):\n",
" if excel_row_updated[columns['Pty Indice'] - 1].value == ws_original.cell(row, columns['Pty Indice']).value:\n",
" new_shipped = excel_row_updated[columns['Shipped'] - 1].value\n",
" new_remain_crit_qty = excel_row_updated[columns['Remain. crit. Qty'] - 1].value\n",
"\n",
" # Update the original workbook\n",
" ws_original.cell(row, columns['Shipped']).value = new_shipped\n",
" ws_original.cell(row, columns['Remain. crit. Qty']).value = new_remain_crit_qty\n",
" ws_original.cell(row, columns['Remain. crit. Qty']).number_format = '0' # Format the cell to show integers only\n",
"\n",
" break\n",
"\n",
"# Save the updated original workbook\n",
"wb_original.save(priority_file_name)\n",
"print(f\"File {priority_file_name} updated successfully.\")\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "ff25b83c-d0c0-4b81-a88f-1125af39b53c",
"metadata": {
"jp-MarkdownHeadingCollapsed": true
},
"source": [
"<h2 style=\"text-align:left;\">Graph creation - WIP </h2> "
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "493c6fe2-9d7f-4cb3-9fa7-c6788ab09d0b",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"File Date: 06-27-2024\n",
"Input files loaded successfully.\n",
"Successfully loaded 'Clear-to-Build-06-27-2024_Formatted.xlsx'\n",
"|Graph| tab created successfully.\n",
"Unique IDD Top Level for Clear to Build: 53\n",
"Unique IDD Top Level for Short: 25\n",
"Sum of 'Backlog row Qty' for 'Standard': 1545\n",
"Sum of 'Backlog row Qty' for 'DX/DO': 1240\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Priority</th>\n",
" <th>Pty Indice</th>\n",
" <th>IDD Top Level</th>\n",
" <th>SEDA Top Level</th>\n",
" <th>Backlog row Qty</th>\n",
" <th>Critical Qty</th>\n",
" <th>Remain. crit. Qty</th>\n",
" <th>Backlog Description</th>\n",
" <th>General Description</th>\n",
" <th>Marge standard</th>\n",
" <th>...</th>\n",
" <th>Currency net amount</th>\n",
" <th>Actual amount -standard</th>\n",
" <th>SO Modified</th>\n",
" <th>Production Status</th>\n",
" <th>Program</th>\n",
" <th>Last Update</th>\n",
" <th>Top-Level Status</th>\n",
" <th>Order Type</th>\n",
" <th>Top-Level Status_y</th>\n",
" <th>Production Status_y</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>P1</td>\n",
" <td>840-000435-1</td>\n",
" <td>351-39193-001</td>\n",
" <td>1</td>\n",
" <td>Completed</td>\n",
" <td>Completed</td>\n",
" <td>CPA ASSY 351-39193-001</td>\n",
" <td>MCP</td>\n",
" <td>-3060.5</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>3060.5</td>\n",
" <td>NaN</td>\n",
" <td>Completed</td>\n",
" <td>Phase 4</td>\n",
" <td>06-27-2024</td>\n",
" <td>NaN</td>\n",
" <td>None</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>P1</td>\n",
" <td>840-000435-1</td>\n",
" <td>351-39193-001</td>\n",
" <td>1</td>\n",
" <td>Completed</td>\n",
" <td>Completed</td>\n",
" <td>CPA ASSY 351-39193-001</td>\n",
" <td>MCP</td>\n",
" <td>-3060.5</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>3060.5</td>\n",
" <td>NaN</td>\n",
" <td>Completed</td>\n",
" <td>Phase 4</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>None</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1</td>\n",
" <td>P1</td>\n",
" <td>840-000435-1</td>\n",
" <td>351-39193-001</td>\n",
" <td>1</td>\n",
" <td>Completed</td>\n",
" <td>Completed</td>\n",
" <td>CPA ASSY 351-39193-001</td>\n",
" <td>MCP</td>\n",
" <td>-3060.5</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>3060.5</td>\n",
" <td>NaN</td>\n",
" <td>Completed</td>\n",
" <td>Phase 4</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>None</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1</td>\n",
" <td>P1</td>\n",
" <td>840-000435-1</td>\n",
" <td>351-39193-001</td>\n",
" <td>11</td>\n",
" <td>Completed</td>\n",
" <td>Completed</td>\n",
" <td>CPA ASSY 351-39193-001</td>\n",
" <td>MCP</td>\n",
" <td>-1334.3</td>\n",
" <td>...</td>\n",
" <td>32331.1</td>\n",
" <td>33665.3</td>\n",
" <td>NaN</td>\n",
" <td>Completed</td>\n",
" <td>Phase 4</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1</td>\n",
" <td>P1</td>\n",
" <td>840-000435-1</td>\n",
" <td>351-39193-001</td>\n",
" <td>10</td>\n",
" <td>Completed</td>\n",
" <td>Completed</td>\n",
" <td>CPA ASSY 351-39193-001</td>\n",
" <td>MCP</td>\n",
" <td>-1213.0</td>\n",
" <td>...</td>\n",
" <td>29391.9</td>\n",
" <td>30604.9</td>\n",
" <td>NaN</td>\n",
" <td>Completed</td>\n",
" <td>Phase 4</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>328</th>\n",
" <td>SIK-New</td>\n",
" <td>SIK-New1</td>\n",
" <td>840-000400-102</td>\n",
" <td>441-42907-301</td>\n",
" <td>1</td>\n",
" <td>TBD</td>\n",
" <td>TBD</td>\n",
" <td>CPA ASSY 441-42907-302</td>\n",
" <td>CPA</td>\n",
" <td>1333.4</td>\n",
" <td>...</td>\n",
" <td>13468.8</td>\n",
" <td>12135.4</td>\n",
" <td>NaN</td>\n",
" <td>Proto & FTB</td>\n",
" <td>SIK-New</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>329</th>\n",
" <td>SIK-New</td>\n",
" <td>SIK-New1</td>\n",
" <td>840-000400-102</td>\n",
" <td>441-42907-301</td>\n",
" <td>1</td>\n",
" <td>TBD</td>\n",
" <td>TBD</td>\n",
" <td>CPA ASSY 441-42907-302</td>\n",
" <td>CPA</td>\n",
" <td>1333.4</td>\n",
" <td>...</td>\n",
" <td>13468.8</td>\n",
" <td>12135.4</td>\n",
" <td>NaN</td>\n",
" <td>Proto & FTB</td>\n",
" <td>SIK-New</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>330</th>\n",
" <td>SIK-New</td>\n",
" <td>SIK-New1</td>\n",
" <td>840-000400-102</td>\n",
" <td>441-42907-301</td>\n",
" <td>1</td>\n",
" <td>TBD</td>\n",
" <td>TBD</td>\n",
" <td>CPA ASSY 441-42907-302</td>\n",
" <td>CPA</td>\n",
" <td>1333.4</td>\n",
" <td>...</td>\n",
" <td>13468.8</td>\n",
" <td>12135.4</td>\n",
" <td>NaN</td>\n",
" <td>Proto & FTB</td>\n",
" <td>SIK-New</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>331</th>\n",
" <td>SIK-New</td>\n",
" <td>SIK-New1</td>\n",
" <td>840-000400-102</td>\n",
" <td>441-42907-301</td>\n",
" <td>1</td>\n",
" <td>TBD</td>\n",
" <td>TBD</td>\n",
" <td>CPA ASSY 441-42907-302</td>\n",
" <td>CPA</td>\n",
" <td>1333.4</td>\n",
" <td>...</td>\n",
" <td>13468.8</td>\n",
" <td>12135.4</td>\n",
" <td>NaN</td>\n",
" <td>Proto & FTB</td>\n",
" <td>SIK-New</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>332</th>\n",
" <td>SIK-New</td>\n",
" <td>SIK-New1</td>\n",
" <td>840-000400-102</td>\n",
" <td>441-42907-301</td>\n",
" <td>1</td>\n",
" <td>TBD</td>\n",
" <td>TBD</td>\n",
" <td>CPA ASSY 441-42907-302</td>\n",
" <td>CPA</td>\n",
" <td>1333.4</td>\n",
" <td>...</td>\n",
" <td>13468.8</td>\n",
" <td>12135.4</td>\n",
" <td>NaN</td>\n",
" <td>Proto & FTB</td>\n",
" <td>SIK-New</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>Standard</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>333 rows × 26 columns</p>\n",
"</div>"
],
"text/plain": [
" Priority Pty Indice IDD Top Level SEDA Top Level Backlog row Qty \\\n",
"0 1 P1 840-000435-1 351-39193-001 1 \n",
"1 1 P1 840-000435-1 351-39193-001 1 \n",
"2 1 P1 840-000435-1 351-39193-001 1 \n",
"3 1 P1 840-000435-1 351-39193-001 11 \n",
"4 1 P1 840-000435-1 351-39193-001 10 \n",
".. ... ... ... ... ... \n",
"328 SIK-New SIK-New1 840-000400-102 441-42907-301 1 \n",
"329 SIK-New SIK-New1 840-000400-102 441-42907-301 1 \n",
"330 SIK-New SIK-New1 840-000400-102 441-42907-301 1 \n",
"331 SIK-New SIK-New1 840-000400-102 441-42907-301 1 \n",
"332 SIK-New SIK-New1 840-000400-102 441-42907-301 1 \n",
"\n",
" Critical Qty Remain. crit. Qty Backlog Description \\\n",
"0 Completed Completed CPA ASSY 351-39193-001 \n",
"1 Completed Completed CPA ASSY 351-39193-001 \n",
"2 Completed Completed CPA ASSY 351-39193-001 \n",
"3 Completed Completed CPA ASSY 351-39193-001 \n",
"4 Completed Completed CPA ASSY 351-39193-001 \n",
".. ... ... ... \n",
"328 TBD TBD CPA ASSY 441-42907-302 \n",
"329 TBD TBD CPA ASSY 441-42907-302 \n",
"330 TBD TBD CPA ASSY 441-42907-302 \n",
"331 TBD TBD CPA ASSY 441-42907-302 \n",
"332 TBD TBD CPA ASSY 441-42907-302 \n",
"\n",
" General Description Marge standard ... Currency net amount \\\n",
"0 MCP -3060.5 ... 0.0 \n",
"1 MCP -3060.5 ... 0.0 \n",
"2 MCP -3060.5 ... 0.0 \n",
"3 MCP -1334.3 ... 32331.1 \n",
"4 MCP -1213.0 ... 29391.9 \n",
".. ... ... ... ... \n",
"328 CPA 1333.4 ... 13468.8 \n",
"329 CPA 1333.4 ... 13468.8 \n",
"330 CPA 1333.4 ... 13468.8 \n",
"331 CPA 1333.4 ... 13468.8 \n",
"332 CPA 1333.4 ... 13468.8 \n",
"\n",
" Actual amount -standard SO Modified Production Status Program \\\n",
"0 3060.5 NaN Completed Phase 4 \n",
"1 3060.5 NaN Completed Phase 4 \n",
"2 3060.5 NaN Completed Phase 4 \n",
"3 33665.3 NaN Completed Phase 4 \n",
"4 30604.9 NaN Completed Phase 4 \n",
".. ... ... ... ... \n",
"328 12135.4 NaN Proto & FTB SIK-New \n",
"329 12135.4 NaN Proto & FTB SIK-New \n",
"330 12135.4 NaN Proto & FTB SIK-New \n",
"331 12135.4 NaN Proto & FTB SIK-New \n",
"332 12135.4 NaN Proto & FTB SIK-New \n",
"\n",
" Last Update Top-Level Status Order Type Top-Level Status_y \\\n",
"0 06-27-2024 NaN None NaN \n",
"1 NaN NaN None NaN \n",
"2 NaN NaN None NaN \n",
"3 NaN NaN Standard NaN \n",
"4 NaN NaN Standard NaN \n",
".. ... ... ... ... \n",
"328 NaN NaN Standard NaN \n",
"329 NaN NaN Standard NaN \n",
"330 NaN NaN Standard NaN \n",
"331 NaN NaN Standard NaN \n",
"332 NaN NaN Standard NaN \n",
"\n",
" Production Status_y \n",
"0 NaN \n",
"1 NaN \n",
"2 NaN \n",
"3 NaN \n",
"4 NaN \n",
".. ... \n",
"328 NaN \n",
"329 NaN \n",
"330 NaN \n",
"331 NaN \n",
"332 NaN \n",
"\n",
"[333 rows x 26 columns]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Charts successfully added to 'Graph_06-27-2024.xlsx'\n"
]
}
],
"source": [
"import xlsxwriter\n",
"import openpyxl\n",
"import pandas as pd\n",
"from openpyxl import load_workbook\n",
"from openpyxl.chart import BarChart, PieChart, Reference\n",
"import re\n",
"from openpyxl.styles import Border, Side, Alignment, Font\n",
"from openpyxl.chart.label import DataLabelList\n",
"from openpyxl.utils.dataframe import dataframe_to_rows\n",
"from openpyxl.worksheet.datavalidation import DataValidation\n",
"from openpyxl.styles import PatternFill, Font, Alignment\n",
"import os\n",
"from openpyxl.chart.text import Text, RichText\n",
"from openpyxl.drawing.text import Paragraph, RegularTextRun\n",
"\n",
"##############################################################################################################################\n",
"# Define date and path\n",
"##############################################################################################################################\n",
"# Define paths and file names\n",
"input_file_formatted = 'Clear-to-Build-06-27-2024_Formatted.xlsx'\n",
"\n",
"# Extract date from the file name using regular expressions\n",
"match = re.search(r'\\d{2}-\\d{2}-\\d{4}', input_file_formatted)\n",
"if match:\n",
" file_date = match.group()\n",
" print(\"File Date:\", file_date)\n",
"else:\n",
" print(\"File Date could not be determined.\")\n",
"\n",
"output_file_name_Graph = f'Graph_{file_date}.xlsx'\n",
"\n",
"#Define Path to Template\n",
"Path = 'Inputs\\Templates'\n",
" \n",
"##############################################################################################################################\n",
"# Load workbook\n",
"##############################################################################################################################\n",
"# Load the Excel files into pandas DataFrames\n",
"try:\n",
" df_Summary = pd.read_excel(input_file_formatted, sheet_name='Summary')\n",
" df_Priority = pd.read_excel(input_file_formatted, sheet_name='CM-Priority')\n",
" df_snapshot = pd.read_excel(input_file_formatted, sheet_name='Snapshot')\n",
" df_TurnoverReport = pd.read_excel(input_file_formatted, sheet_name='CM-TurnoverReport')\n",
" df_backlog = pd.read_excel(input_file_formatted, sheet_name='CM-Backlog')\n",
" print(\"Input files loaded successfully.\")\n",
"except FileNotFoundError as e:\n",
" print(f\"File not found: {e}\")\n",
" exit()\n",
"\n",
"# Load the workbook\n",
"try:\n",
" workbook = load_workbook(input_file_formatted)\n",
" print(f\"Successfully loaded '{input_file_formatted}'\")\n",
"except FileNotFoundError as e:\n",
" print(f\"File not found: {e}\")\n",
" exit()\n",
"\n",
"#######################################################\n",
"# Define function to get chart titles and indices\n",
"######################################################\n",
"def extract_text_from_chart_text(chart_text):\n",
" \"\"\"\n",
" Extracts text from chart title objects which may be rich text or simple strings.\n",
" \"\"\"\n",
" if isinstance(chart_text, Text):\n",
" return chart_text.text if chart_text.text else None\n",
" elif isinstance(chart_text, RichText):\n",
" paragraphs = chart_text.p\n",
" if paragraphs:\n",
" text_parts = []\n",
" for paragraph in paragraphs:\n",
" for run in paragraph.r:\n",
" if isinstance(run, RegularTextRun):\n",
" text_parts.append(run.t)\n",
" elif isinstance(run, str):\n",
" text_parts.append(run)\n",
" return \" \".join(text_parts)\n",
" elif isinstance(chart_text, str): # Handle cases where title is a simple string\n",
" return chart_text.strip()\n",
"\n",
" return None\n",
"\n",
"def get_chart_properties(file_path, sheet_name):\n",
" try:\n",
" wb = load_workbook(file_path, data_only=True)\n",
" sheet = wb[sheet_name]\n",
"\n",
" chart_properties = []\n",
"\n",
" if hasattr(sheet, '_charts'): # Check if the sheet has charts\n",
" for idx, chart in enumerate(sheet._charts, start=1):\n",
" chart_type = type(chart).__name__\n",
" chart_title = extract_text_from_chart_text(chart.title)\n",
" chart_properties.append({\n",
" 'Chart Index': idx,\n",
" 'Chart Type': chart_type,\n",
" 'Chart Title': chart_title if chart_title else 'No title'\n",
" })\n",
" else:\n",
" print(f\"No charts found in sheet '{sheet_name}'.\")\n",
"\n",
" return chart_properties\n",
"\n",
" except FileNotFoundError:\n",
" print(f\"File '{file_path}' not found.\")\n",
" return []\n",
"\n",
" except Exception as e:\n",
" print(f\"Error occurred while processing '{file_path}': {e}\")\n",
" return []\n",
"\n",
"'''\n",
"# Example usage:\n",
"file_name_Template = 'Graph_Template.xlsx' # Replace with your actual file name\n",
"sheet_name = 'Graph' # Replace with your actual sheet name\n",
"file_path = os.path.join('Inputs', 'Templates', file_name_Template) # Adjust the path as necessary\n",
"\n",
"chart_properties = get_chart_properties(file_path, sheet_name)\n",
"\n",
"if chart_properties:\n",
" print(f\"Charts found in sheet '{sheet_name}':\")\n",
" for chart in chart_properties:\n",
" print(f\"Chart {chart['Chart Index']} - Type: {chart['Chart Type']}\")\n",
" print(f\"Chart {chart['Chart Index']} - Title: {chart['Chart Title']}\")\n",
"else:\n",
" print(f\"No charts found in sheet '{sheet_name}'.\")\n",
"'''\n",
"#******************************************************************************************************************************\n",
"##############################################################################################################################\n",
"#Create |Graph|\n",
"##############################################################################################################################\n",
"#******************************************************************************************************************************\n",
"# Check if 'Graph' sheet already exists and replace it\n",
"if 'Graph' in workbook.sheetnames:\n",
" # Delete existing 'Graph' sheet\n",
" workbook.remove(workbook['Graph'])\n",
"\n",
"# Create 'Graph' sheet\n",
"graph_sheet = workbook.create_sheet(title='Graph', index = 0)\n",
"print(\"|Graph| tab created successfully.\") \n",
"\n",
"#********************************************************************************************************************************\n",
"# Divide |Graph| in 2 section --> |Project History| |General Overview| |Clear to Build Overview| |Progression Overview| \n",
" # | Project Status |\n",
"#******************************************************************************************************************************\n",
"# Write title \"Project History\" in cell A1\n",
"graph_sheet['A1'] = 'Project History'\n",
"graph_sheet['A1'].font = Font(bold=True)\n",
"graph_sheet['A1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells A1 to N1\n",
"graph_sheet.merge_cells('A1:M1')\n",
"\n",
"# Write title \"General Overview\" in cell N1\n",
"graph_sheet['N1'] = 'General Overview'\n",
"graph_sheet['N1'].font = Font(bold=True)\n",
"graph_sheet['N1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells N1 to Z1\n",
"graph_sheet.merge_cells('N1:Z1')\n",
"\n",
"# Write title \"General Overview\" in cell AA1\n",
"graph_sheet['AA1'] = 'Clear to Build Overview'\n",
"graph_sheet['AA1'].font = Font(bold=True)\n",
"graph_sheet['AA1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells AA1 to AM1\n",
"graph_sheet.merge_cells('AA1:AM1')\n",
"\n",
"# Write title \"Progression Overview\" in cell AN1\n",
"graph_sheet['AN1'] = 'Progression Overview'\n",
"graph_sheet['AN1'].font = Font(bold=True)\n",
"graph_sheet['AN1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells AN1 to A1:AZ1\n",
"graph_sheet.merge_cells('AN1:AZ1')\n",
"\n",
"# Write title \"Project Status\" in cell A79\n",
"graph_sheet['A79'] = 'Project Status'\n",
"graph_sheet['A79'].font = Font(bold=True)\n",
"graph_sheet['A79'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells A79 to AZ79\n",
"graph_sheet.merge_cells('A79:AZ79')\n",
"\n",
"####################\n",
"#Formatting row 1\n",
"##################\n",
"# Define fill color (blue)\n",
"fill = PatternFill(start_color='8DB4E2', end_color='8DB4E2', fill_type='solid')\n",
"\n",
"# Font properties (white color, size 16)\n",
"font = Font(color='FFFFFF', size=16)\n",
"\n",
"# Apply styles to row 1\n",
"for col in range(1, graph_sheet.max_column + 1):\n",
" cell = graph_sheet.cell(row=1, column=col)\n",
" cell.fill = fill\n",
" cell.font = font\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
"\n",
"####################\n",
"#Formatting row 79\n",
"##################\n",
"# Apply styles to row 79\n",
"for col in range(1, graph_sheet.max_column + 1):\n",
" cell = graph_sheet.cell(row=79, column=col)\n",
" cell.fill = fill\n",
" cell.font = font\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" \n",
"###############################\n",
"# Graph Label number\n",
"###############################\n",
"'''\n",
"graph_sheet['A2'] = 1\n",
"graph_sheet['A27'] = 2\n",
"graph_sheet['A52'] = 3\n",
"graph_sheet['N2'] = 4\n",
"graph_sheet['N27'] = 5\n",
"graph_sheet['N52'] = 6\n",
"graph_sheet['AA2'] = 7\n",
"graph_sheet['AA27'] = 8\n",
"graph_sheet['AA52'] = 9\n",
"graph_sheet['AN2'] = 10\n",
"graph_sheet['AN27'] = 11\n",
"graph_sheet['AN52'] = 12\n",
"graph_sheet['A80'] = 13\n",
"graph_sheet['A115'] = 14\n",
"'''\n",
"\n",
"# Write numbers in specified positions using indices\n",
"positions = [(2, 1, 1), (2, 14, 4), (2, 27, 7), (27, 1, 2), (27, 14, 5), (27, 27, 8), (2, 40, 10), (27, 40, 11), (52, 1, 3), (52, 14, 6), (52, 27, 9), (52, 40, 12), (80, 1, 13), (115, 1, 14)] # (row, column, 'Number')\n",
"for row_idx, col_idx, number in positions:\n",
" cell = graph_sheet.cell(row=row_idx, column=col_idx)\n",
" cell.value = number\n",
" cell.fill = fill\n",
" cell.font = Font(color='FFFFFF', size=14, bold=True)\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" \n",
"#########################################################################\n",
"# Set the width of column A to 2.5 (or any desired width)\n",
"graph_sheet.column_dimensions['A'].width = 4\n",
"\n",
"# Set the width of column N to 2 (or any desired width)\n",
"graph_sheet.column_dimensions['N'].width = 4\n",
"\n",
"# Set the width of column AA to 2 (or any desired width)\n",
"graph_sheet.column_dimensions['AA'].width = 4\n",
"\n",
"# Set the width of column AN to 2 (or any desired width)\n",
"graph_sheet.column_dimensions['AN'].width = 4\n",
"\n",
"##########################################################################################\n",
"## Horizontal tick border column N, AB, AM, AZ/ Vertical dashDot border A30:AZ30, A52:AZ52\n",
"##########################################################################################\n",
"# Define a thick border style for the upper border\n",
"thick_border_upper = Border(top=Side(style='thick',color='000000'))\n",
"\n",
"# Apply the thick border to row 2 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=2, column=col)\n",
" cell.border = thick_border_upper\n",
"\n",
"# Define a dashdot border style for the upper border\n",
"dash_border_upper = Border(top=Side(style='dashDot',color='808080'))\n",
"\n",
"# Apply the Dash border to row 27 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=27, column=col)\n",
" cell.border = dash_border_upper\n",
"\n",
"# Apply the Dash border to row 52 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=52, column=col)\n",
" cell.border = dash_border_upper\n",
"\n",
"#####################################\n",
"### Define vertical thick border style\n",
"##########################################\n",
"thick_border_left = Border(left=Side(style='thick'))\n",
"\n",
"# Apply the thick border to column N from row 1 to 79\n",
"for row in range(1, 80):\n",
" cell_N = graph_sheet.cell(row=row, column=14) # Column N is the 14th column\n",
" cell_N.border = thick_border_left\n",
"\n",
"# Apply the thick border to column AA from row 1 to 79\n",
"for row in range(1, 80):\n",
" cell_AA = graph_sheet.cell(row=row, column=27) # Column AA is the 27th column\n",
" cell_AA.border = thick_border_left\n",
"\n",
"# Apply the thick border to column AM from row 1 to 79\n",
"for row in range(1, 80):\n",
" cell_AM = graph_sheet.cell(row=row, column=40) # Column AM is the 40th column\n",
" cell_AM.border = thick_border_left\n",
"\n",
"# Apply the thick border to column AZ from row 1 to 150\n",
"for row in range(1, 149):\n",
" cell_AM = graph_sheet.cell(row=row, column=53) # Column AZ is the 53th column\n",
" cell_AM.border = thick_border_left\n",
"\n",
"############################\n",
"#Combined Left/Upper border \n",
"############################\n",
"# Apply both the thick left and upper borders to cell N27\n",
"cell_N2 = graph_sheet.cell(row=2, column=14) # Column N is the 14th column\n",
"cell_N2.border = Border(left=thick_border_left.left, top=thick_border_upper.top)\n",
"\n",
"# Apply both the thick left and DashDot upper borders to cell AA27\n",
"cell_AA27= graph_sheet.cell(row=27, column=27) # Column AA is the 27th column\n",
"cell_AA27.border = Border(left=thick_border_left.left, top=dash_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell N27\n",
"cell_N27 = graph_sheet.cell(row=27, column=14) # Column N is the 14th column\n",
"cell_N27.border = Border(left=thick_border_left.left, top=dash_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell AA2\n",
"cell_AA2 = graph_sheet.cell(row=2, column=27) # Column AA is the 28th column\n",
"cell_AA2.border = Border(left=thick_border_left.left, top=thick_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell AN27\n",
"cell_AN27 = graph_sheet.cell(row=27, column=40) # Column N is the 14th column\n",
"cell_AN27.border = Border(left=thick_border_left.left, top=dash_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell AN2\n",
"cell_AN2 = graph_sheet.cell(row=2, column=40) # Column AN is the 28th column\n",
"cell_AN2.border = Border(left=thick_border_left.left, top=thick_border_upper.top)\n",
"\n",
"################################################\n",
"# Horizantal Border for section |Project Status|\n",
"###############################################\n",
"# Apply the thick border to row 79 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=79, column=col)\n",
" cell.border = thick_border_upper\n",
"\n",
"# Apply the thick border to row 80 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=80, column=col)\n",
" cell.border = thick_border_upper\n",
" \n",
"###################################\n",
"#****************************************************************************************************************************\n",
"# CHARTS \n",
"#****************************************************************************************************************************\n",
"#####################################################\n",
"#Create a new dataframe including the completed PN\n",
"#######################################################\n",
"#Include the Pty Indice not in |Snapshot| (df_Snapshot) but present in |CM-Priority| (df_Priority) with 'Production Status' = 'Completed' \n",
"#Include in a new df_Snapshot_Priority the column 'Description','Production Status', 'IDD Sale Price', 'SEDA Sale Price' from CM-Priority\n",
"df_Priority_filtered = df_Priority[df_Priority['Production Status'] == 'Completed'][['Pty Indice', 'Priority', 'IDD Top Level', 'SEDA Top Level', 'Shipped', 'Remain. crit. Qty','Description', 'Production Status', 'SEDA Sale Price']]\n",
"\n",
"#Merge df_snapshot with df_Priority_filtered based on 'Pty Indice'\n",
"df_snapshot_priority = pd.concat([df_snapshot, df_Priority_filtered], ignore_index=True)\n",
"\n",
"# Fill NaN values in 'Top-Level Status' with 'Completed' for the newly merged rows\n",
"df_snapshot_priority['Top-Level Status'].fillna('Completed', inplace=True)\n",
"\n",
"#Aplly function to fill the 'Product Category' \n",
"def determine_category(description):\n",
" if not isinstance(description, str):\n",
" return 'Others'\n",
" if description == 'Rototellite':\n",
" return 'Rototellite'\n",
" elif 'Indicator' in description or 'CPA' in description:\n",
" return 'CPA'\n",
" elif 'Lightplate' in description:\n",
" return 'Lightplate'\n",
" elif 'ISP' in description or 'Keyboard' in description:\n",
" return 'ISP'\n",
" elif 'Module' in description:\n",
" return 'CPA'\n",
" elif 'optics' in description:\n",
" return 'Fiber Optics'\n",
" else:\n",
" return 'Others'\n",
"\n",
"# Apply the determine_category function to 'Description' column\n",
"df_snapshot_priority['Product Category'] = df_snapshot_priority['Description'].apply(determine_category)\n",
"\n",
"#display(df_snapshot_priority)\n",
"\n",
"####################################################################################\n",
"#***************************************************************************************************************************\n",
"#|Project History|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#1 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"category_counts = df_snapshot_priority['Product Category'].value_counts(normalize=True) * 100\n",
"category_percentages = category_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 3, column 2 (B3)\n",
"graph_sheet.cell(row=3, column=2, value=\"Product Category\")\n",
"graph_sheet.cell(row=3, column=3, value=\"Percentage\")\n",
"\n",
"# Write category and percentage data to the worksheet\n",
"for idx, (category, percentage) in enumerate(category_percentages.items(), start=4):\n",
" graph_sheet.cell(row=idx, column=2, value=category)\n",
" graph_sheet.cell(row=idx, column=3, value=percentage)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Product Categories\\nInputs date: {file_date} - Source: |CM-Priority|\"\n",
"\n",
"# Create a pie chart\n",
"chart1 = PieChart()\n",
"chart1.title = chart_title\n",
"\n",
"################################################\n",
"# Prepare data for the pie chart using Reference\n",
"####################################################\n",
"min_row = 3 # Start from row 3 where your data starts\n",
"max_row = min_row + len(category_percentages) # Calculate the last row based on the number of categories\n",
"min_row_label = 4 # Start from row 4 for labels\n",
"max_row_label = min_row_label + len(category_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=3, min_row=min_row, max_row=max_row) # Adjusted min_col to 3\n",
"labels = Reference(graph_sheet, min_col=2, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 2\n",
"\n",
"chart1.add_data(data_points, titles_from_data=True)\n",
"chart1.set_categories(labels)\n",
" \n",
"#############################\n",
"# Set size/position\n",
"##############################\n",
"chart1.width = 20 # Adjust the width as needed\n",
"chart1.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning \n",
"graph_sheet.add_chart(chart1, \"B3\")\n",
"\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#2 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"status_counts = df_snapshot_priority['Production Status'].value_counts(normalize=True) * 100\n",
"status_percentages = status_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 28, column 2 (B28)\n",
"graph_sheet.cell(row=28, column=2, value=\"Production Status\")\n",
"graph_sheet.cell(row=28, column=3, value=\"Percentage\")\n",
"\n",
"# Write category and percentage data to the worksheet\n",
"for idx, (status, percentage) in enumerate(status_percentages.items(), start=28): # Start index corrected to 28\n",
" graph_sheet.cell(row=idx + 1, column=2, value=status) # Adjusted row index and added +1 to start from 29\n",
" graph_sheet.cell(row=idx + 1, column=3, value=percentage) # Adjusted row index and added +1 to start from 29\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Production Status\\nInputs date: {file_date} - Source: |CM-Priority|\"\n",
"\n",
"# Create a pie chart\n",
"chart2 = PieChart()\n",
"chart2.title = chart_title\n",
"################################################\n",
"\n",
"# Prepare data for the pie chart using Reference\n",
"min_row = 28 # Start from row 28 to include the data\n",
"max_row = min_row + len(status_percentages) # Calculate the last row based on the number of statuses\n",
"min_row_label = 29 # Start from row 29 for labels\n",
"max_row_label = min_row_label + len(status_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=3, min_row=min_row, max_row=max_row) # Adjusted min_col to 3\n",
"labels = Reference(graph_sheet, min_col=2, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 2\n",
"\n",
"chart2.add_data(data_points, titles_from_data=True)\n",
"chart2.set_categories(labels)\n",
"\n",
"# Set the size of the chart\n",
"chart2.width = 20 # Adjust the width as needed\n",
"chart2.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart\n",
"graph_sheet.add_chart(chart2, \"B28\")\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#3 []\n",
"##############################################################################################################################\n",
"\n",
"\n",
"\n",
"#***************************************************************************************************************************\n",
"#|General Overview|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#4 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"category_counts = df_snapshot['Product Category'].value_counts(normalize=True) * 100\n",
"category_percentages = category_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 3, column 15 (O3)\n",
"graph_sheet.cell(row=3, column=15, value=\"Product Category\")\n",
"graph_sheet.cell(row=3, column=16, value=\"Percentage\")\n",
"\n",
"# Write category and percentage data to the worksheet\n",
"for idx, (category, percentage) in enumerate(category_percentages.items(), start=4):\n",
" graph_sheet.cell(row=idx, column=15, value=category)\n",
" graph_sheet.cell(row=idx, column=16, value=percentage)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Product Categories\\nInputs date: {file_date} - Source: |CM-Snapshot|\"\n",
"\n",
"# Create a pie chart\n",
"chart4 = PieChart()\n",
"chart4.title = chart_title\n",
"\n",
"##########################################################\n",
"# Prepare data for the pie chart using Reference\n",
"min_row = 3 # Start from row 3 where your data starts\n",
"max_row = min_row + len(category_percentages) # Calculate the last row based on the number of categories\n",
"min_row_label = 4 # Start from row 4 for labels\n",
"max_row_label = min_row_label + len(category_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=16, min_row=min_row, max_row=max_row) # Adjusted min_col to 16\n",
"labels = Reference(graph_sheet, min_col=15, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 15\n",
"\n",
"chart4.add_data(data_points, titles_from_data=True)\n",
"chart4.set_categories(labels)\n",
" \n",
"#############################\n",
"# Set the size of the chart4\n",
"##############################\n",
"chart4.width = 20 # Adjust the width as needed\n",
"chart4.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart4 \n",
"graph_sheet.add_chart(chart4, \"O3\")\n",
"##############################################################################################################################\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#5 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"status_counts = df_snapshot['Production Status'].value_counts(normalize=True) * 100\n",
"status_percentages = status_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 28, column 15 (O28)\n",
"graph_sheet.cell(row=28, column=15, value=\"Production Status\")\n",
"graph_sheet.cell(row=28, column=16, value=\"Percentage\")\n",
"\n",
"# Write status and percentage data to the worksheet starting from row 29\n",
"for idx, (status, percentage) in enumerate(status_percentages.items(), start=29):\n",
" graph_sheet.cell(row=idx, column=15, value=status)\n",
" graph_sheet.cell(row=idx, column=16, value=percentage)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Production Status\\nInputs date: {file_date} - Source: |CM-Snapshot|\"\n",
"\n",
"chart5 = PieChart()\n",
"chart5.title = chart_title\n",
"\n",
"############################################################\n",
"# Prepare data for the pie chart using Reference\n",
"min_row = 28 # Start from row 29 to include the data\n",
"max_row = min_row + len(status_percentages) # Calculate the last row based on the number of statuses\n",
"min_row_label = 29 # Start from row 29 for labels\n",
"max_row_label = min_row_label + len(status_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=16, min_row=min_row, max_row=max_row) # Adjusted min_col to 16\n",
"labels = Reference(graph_sheet, min_col=15, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 15\n",
"\n",
"chart5.add_data(data_points, titles_from_data=True)\n",
"chart5.set_categories(labels)\n",
" \n",
"#############################\n",
"# Set size/position\n",
"##############################\n",
"chart5.width = 20 # Adjust the width as needed\n",
"chart5.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning \n",
"graph_sheet.add_chart(chart5, \"O28\")\n",
"\n",
"##############################################################################################################################\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#6 []\n",
"##############################################################################################################################\n",
"\n",
"\n",
"#***************************************************************************************************************************\n",
"#|Clear to Build Overview|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#7 []\n",
"##############################################################################################################################\n",
"# Calculate unique IDD Top Level for each Product Category and each classification\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index='Product Category',\n",
" columns='Top-Level Status',\n",
" values='IDD Top Level',\n",
" aggfunc=pd.Series.nunique,\n",
" fill_value=0)\n",
"\n",
"# Calculate total unique IDD Top Level across all categories\n",
"total_unique_idd_top_level = df_snapshot['IDD Top Level'].nunique()\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 3\n",
"start_cell_col = 28 # Column 'AB3'\n",
"\n",
"# Write headers\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col, value=\"Product Category\")\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 1, value=\"Unique IDD Top Level (Clear-to-Build)\")\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 2, value=\"Unique IDD Top Level (Short)\")\n",
"\n",
"# Write data rows\n",
"for r_idx, (index, row) in enumerate(pivot_table_df.iterrows(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=index) # Write Product Category\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 1, value=row['Clear-to-Build']) # Write unique count for Clear-to-Build\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 2, value=row['Short']) # Write unique count for Short\n",
"\n",
"# Add the total unique IDD Top Level label and value in the last row\n",
"total_row = start_cell_row + len(pivot_table_df) + 1\n",
"graph_sheet.cell(row=total_row, column=start_cell_col, value=\"Total Unique IDD Top Level\")\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 1, value=pivot_table_df['Clear-to-Build'].sum())\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 2, value=pivot_table_df['Short'].sum())\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Count of Unique IDD Top Level by Product Category\\nInputs date: {file_date} - Source: |Snapshot|\"\n",
"\n",
"# Create a bar chart (chart6)\n",
"chart7 = BarChart()\n",
"chart7.title = chart_title\n",
"chart7.x_axis.title = 'Product Category'\n",
"chart7.y_axis.title = 'Count of Unique IDD Top Level'\n",
"\n",
"##################################################\n",
"# Define data for the chart (exclude totals row)\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 2,\n",
" max_row=start_cell_row + len(pivot_table_df) + 1)\n",
"chart7.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_row=start_cell_row + len(pivot_table_df) + 1)\n",
"chart7.set_categories(categories)\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart7.width = 20 # Adjust the width as needed\n",
"chart7.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart7, \"AB3\")\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#8 []\n",
"##############################################################################################################################\n",
"# Create a pivot table with 'Product Category' as index\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index='Product Category',\n",
" columns='Top-Level Status',\n",
" values='IDD Backlog Qty',\n",
" aggfunc='sum',\n",
" fill_value=0)\n",
"\n",
"# Calculate unique IDD Top Level for Clear to Build and Short\n",
"unique_idd_top_level_ctb = df_snapshot[df_snapshot['Top-Level Status'] == 'Clear-to-Build']['IDD Top Level'].nunique()\n",
"unique_idd_top_level_short = df_snapshot[df_snapshot['Top-Level Status'] == 'Short']['IDD Top Level'].nunique()\n",
"\n",
"print('Unique IDD Top Level for Clear to Build:', unique_idd_top_level_ctb)\n",
"print('Unique IDD Top Level for Short:', unique_idd_top_level_short)\n",
"\n",
"# Write the pivot table to the graph_sheet starting from AB28\n",
"start_cell_row = 28\n",
"start_cell_col = 28 # Column 'AB28'\n",
"\n",
"# Write headers for each column first\n",
"for c_idx, col in enumerate(pivot_table_df.columns, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Write data rows\n",
"for r_idx, (index, row) in enumerate(pivot_table_df.iterrows(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=index) # Write Product Category\n",
" for c_idx, value in enumerate(row, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"# Add the total unique IDD Top Level labels and values in the second row\n",
"total_row = start_cell_row + 1\n",
"graph_sheet.cell(row=total_row, column=start_cell_col, value=\"Total unique IDD Top Level\")\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 1, value=unique_idd_top_level_ctb)\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 2, value=unique_idd_top_level_short)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Backlog Qty by Product Category and Top-Level Status\\nInputs date: {file_date} - Source: |Snapshot|\"\n",
"\n",
"chart8 = BarChart()\n",
"chart8.title = chart_title\n",
"chart8.x_axis.title = 'Product Category'\n",
"chart8.y_axis.title = 'IDD Backlog Qty'\n",
"\n",
"##################################################\n",
"# Define data for the chart including total unique IDD Top Level and all rows\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row, \n",
" max_col=start_cell_col + len(pivot_table_df.columns),\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"chart8.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis) including the total unique IDD Top Level label and all rows\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1, \n",
" max_row=start_cell_row + len(pivot_table_df)) \n",
"chart8.set_categories(categories)\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart8.width = 20 # Adjust the width as needed\n",
"chart8.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart8, \"AB28\")\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#9 []\n",
"##############################################################################################################################\n",
"# Merge df_backlog with df_snapshot to bring 'Top-Level Status' into df_backlog\n",
"df_backlog = pd.merge(df_backlog, df_snapshot[['Pty Indice', 'Top-Level Status']], on='Pty Indice', how='left')\n",
"\n",
"# Define 'Order Type' column based on 'Order' column, excluding rows containing 'NC'\n",
"df_backlog['Order Type'] = df_backlog['Order'].apply(lambda x: 'DX/DO' if str(x).startswith('D') else ('Standard' if 'NC' not in str(x) else None))\n",
"\n",
"# Calculate sum of 'Backlog row Qty' by 'Pty Indice' and 'Order Type'\n",
"#df_backlog['Sum Backlog Qty'] = df_backlog.groupby(['Pty Indice', 'Order Type'])['Backlog row Qty'].transform('sum')\n",
"\n",
"#display(df_backlog) \n",
"\n",
"\n",
"# Calculate sum of 'Backlog row Qty' separately for 'Standard' and 'DX/DO'\n",
"sum_standard = df_backlog.loc[df_backlog['Order Type'] == 'Standard', 'Backlog row Qty'].sum()\n",
"sum_dx_do = df_backlog.loc[df_backlog['Order Type'] == 'DX/DO', 'Backlog row Qty'].sum()\n",
"\n",
"print(f\"Sum of 'Backlog row Qty' for 'Standard': {sum_standard}\")\n",
"print(f\"Sum of 'Backlog row Qty' for 'DX/DO': {sum_dx_do}\")\n",
"\n",
"#Create pivot table\n",
"pivot_table_df = pd.pivot_table(df_backlog,\n",
" index='Order Type',\n",
" columns='Top-Level Status',\n",
" values='Backlog row Qty',\n",
" aggfunc='sum',\n",
" fill_value=0)\n",
"\n",
"# Write the pivot table to the graph_sheet starting from AB53\n",
"start_cell_row = 53\n",
"start_cell_col = 28 # Column AB\n",
"\n",
"# Write headers for each column first\n",
"for c_idx, col in enumerate(pivot_table_df.columns, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Write data rows\n",
"for r_idx, (index, row) in enumerate(pivot_table_df.iterrows(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=index) # Write Order Type\n",
" for c_idx, value in enumerate(row, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"##################################\n",
"#Create and configure the chart\n",
"######################################\n",
"chart_title = f\"IDD Backlog Qty by type of order\\nInputs date: {file_date} - Source: |CM-Backlog|\"\n",
"\n",
"chart9 = BarChart()\n",
"chart9.title = chart_title\n",
"chart9.x_axis.title = 'Order Type'\n",
"chart9.y_axis.title = 'IDD Backlog Qty'\n",
"########################################\n",
"\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row, \n",
" max_col=start_cell_col + len(pivot_table_df.columns),\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"chart9.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1, \n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"chart9.set_categories(categories)\n",
"\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart9.width = 20 # Adjust the width as needed\n",
"chart9.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart9, \"AB53\")\n",
"\n",
"#***************************************************************************************************************************\n",
"#|Progression Overview|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#10 []\n",
"##############################################################################################################################\n",
"# Filter to exclude rows where 'Order' contains 'NC'\n",
"filtered_df = df_TurnoverReport[~df_TurnoverReport['Order'].str.contains('NC')]\n",
"\n",
"# Define the span period of the report\n",
"start_date = filtered_df['Invoice date'].min()\n",
"end_date = filtered_df['Invoice date'].max()\n",
"span_period = f\"{start_date} to {end_date}\"\n",
"\n",
"# Group by 'Pty Indice' and sum 'TurnoverReport row Qty'\n",
"sum_qty_by_indice = filtered_df.groupby('Pty Indice')['TurnoverReport row Qty'].sum()\n",
"\n",
"# Print or display the result\n",
"#print(sum_qty_by_indice)\n",
"\n",
"# Write sum_qty_by_indice to Excel starting from cell AO3\n",
"start_cell_row = 3\n",
"start_cell_col = 41 # Column 'AO'\n",
"\n",
"# Write headers\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col, value='Pty Indice')\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 1, value='Qty shipped')\n",
"\n",
"# Write data rows\n",
"for r_idx, (indice, sum_qty) in enumerate(sum_qty_by_indice.items(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=indice)\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 1, value=sum_qty)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Qty shipped by Pty Indice - {span_period}\\nInputs date: {file_date} - Source: |CM-TurnoverReport|\"\n",
"\n",
"chart10 = BarChart()\n",
"chart10.title = chart_title #f'Qty shipped by Pty Indice - {span_period}'\n",
"chart10.x_axis.title = 'Pty Indice'\n",
"chart10.y_axis.title = 'Qty shipped'\n",
"\n",
"###################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart10.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart10.set_categories(categories)\n",
"\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart10.width = 20 # Adjust the width as needed\n",
"chart10.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart10, \"AO3\")\n",
"\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#11 []\n",
"##############################################################################################################################\n",
"# Filter to include only rows where 'Order' contains 'NC'\n",
"filtered_df = df_TurnoverReport[df_TurnoverReport['Order'].str.contains('NC')].copy()\n",
"\n",
"# Categorize 'TurnoverReport row Qty' as 'Shipped' or 'Received'\n",
"filtered_df['Category'] = filtered_df['TurnoverReport row Qty'].apply(lambda x: 'Shipped' if x > 0 else 'Received')\n",
"\n",
"# Group by 'Pty Indice' and sum 'TurnoverReport row Qty'\n",
"sum_qty_by_indice = filtered_df.groupby('Pty Indice')['TurnoverReport row Qty'].sum()\n",
"\n",
"# Create a pivot table with 'Pty Indice' as index and 'Category' as columns\n",
"pivot_table_df = pd.pivot_table(filtered_df,\n",
" index='Pty Indice',\n",
" columns='Category',\n",
" values='TurnoverReport row Qty',\n",
" aggfunc='sum',\n",
" fill_value=0)\n",
"\n",
"# Write sum_qty_by_indice to Excel starting from cell AO28\n",
"start_cell_row = 28\n",
"start_cell_col = 41 # Column 'AO'\n",
"\n",
"# Write headers\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col, value='Pty Indice')\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 1, value='Qty shipped')\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 2, value='Category')\n",
"\n",
"# Write data rows\n",
"for r_idx, (indice, sum_qty) in enumerate(sum_qty_by_indice.items(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=indice)\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 1, value=sum_qty)\n",
" # Assigning the category based on sum_qty\n",
" category = 'Shipped' if sum_qty > 0 else 'Received'\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 2, value=category)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Qty shipped by Pty Indice - {span_period}\\nInputs date: {file_date} - Source: |CM-TurnoverReport|\"\n",
"\n",
"chart11 = BarChart()\n",
"chart11.title = chart_title #f'Qty shipped by Pty Indice - {span_period}'\n",
"chart11.x_axis.title = 'Pty Indice'\n",
"chart11.y_axis.title = 'Qty shipped'\n",
"\n",
"#################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart11.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart11.set_categories(categories)\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart11.width = 20 # Adjust the width as needed\n",
"chart11.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart11, \"AO28\")\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#12 []\n",
"##############################################################################################################################\n",
"\n",
"\n",
"\n",
"\n",
"#***************************************************************************************************************************\n",
"#|Project Status|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#13 []\n",
"##############################################################################################################################\n",
"# Create a new column 'Product status' based on the condition\n",
"df_snapshot['Industrialization'] = df_snapshot['Production Status'].apply(lambda x: 'Industrialized' if x.strip() == 'Industrialized' else 'Not Industrialized')\n",
"\n",
"# Create a pivot table with 'Product Category', 'Pty Indice', and 'Top-Level Status' as index\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index=['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice'],\n",
" values=['IDD Backlog Qty', 'Remain. crit. Qty', 'Qty clear to build'],\n",
" aggfunc='sum',\n",
" fill_value=0).reset_index()\n",
"\n",
"# Sort pivot_table_df by 'Top-Level Status', 'Industrialization', then 'Product Category'\n",
"sort_order = ['Clear-to-Build', 'Short']\n",
"pivot_table_df['Top-Level Status'] = pd.Categorical(pivot_table_df['Top-Level Status'], categories=sort_order, ordered=True)\n",
"pivot_table_df['Industrialization'] = pd.Categorical(pivot_table_df['Industrialization'], categories=['Industrialized', 'Not Industrialized'], ordered=True)\n",
"pivot_table_df.sort_values(by=['Top-Level Status', 'Industrialization', 'Product Category'], inplace=True)\n",
"\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 81\n",
"start_cell_col = 2 # Column 'B81'\n",
"\n",
"# Write headers for each column\n",
"headers = ['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice', 'IDD Backlog Qty', 'Remain. crit. Qty', 'Qty clear to build']\n",
"for c_idx, col in enumerate(headers, start=start_cell_col):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Initialize a set to track written statuses and categories\n",
"written_statuses_categories = set()\n",
"\n",
"# Write data rows\n",
"for r_idx, row in pivot_table_df.iterrows():\n",
" current_status = row['Top-Level Status']\n",
" current_industrialization = row['Industrialization']\n",
" current_category = row['Product Category']\n",
" \n",
" # Write Top-Level Status and Industrialization only if it's the first occurrence\n",
" if (current_status, current_industrialization) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col, value=current_status)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 1, value=current_industrialization)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization)) # Add current_status and current_industrialization to written_statuses\n",
" \n",
" # Write Product Category only if Status, Industrialization, and Category are first occurrence\n",
" if (current_status, current_industrialization, current_category) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 2, value=current_category)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization, current_category)) # Add (current_status, current_industrialization, current_category) to written_statuses\n",
" \n",
" # Write Pty Indice\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 3, value=row['Pty Indice'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write IDD Backlog Qty\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4, value=row['IDD Backlog Qty'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write Remain. crit. Qty \n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 5, value=row['Remain. crit. Qty'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write Qty clear to build\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 6, value=row['Qty clear to build'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Top-Level Status, Production Status & Product Category\\nInputs date: {file_date} - Source: |CM-Snapshot|\"\n",
"\n",
"chart13 = BarChart()\n",
"chart13.title = \"IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Top-Level Status, Production Status & Product Category\"\n",
"chart13.x_axis.title = None\n",
"chart13.y_axis.title = 'IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build '\n",
"\n",
"########################################################\n",
"# Define data for the chartDark \n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 4,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 6,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart13.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_col=start_cell_col + 3,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart13.set_categories(categories)\n",
"\n",
"############################\n",
"# Set the size of the chart\n",
"############################\n",
"chart13.width = 80 # Adjust the width as needed\n",
"chart13.height = 16 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart13, \"B81\")\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#14 []\n",
"##############################################################################################################################\n",
"# Calculate IDD Total Sales\n",
"df_snapshot['IDD Total Sales'] = df_snapshot['IDD Backlog Qty'] * df_snapshot['IDD Sale Price']\n",
"\n",
"# Calculate IDD Total Marge\n",
"df_snapshot['IDD Total Marge'] = df_snapshot['IDD Backlog Qty'] * df_snapshot['IDD Marge Standard (unit)']\n",
"\n",
"# Create a new column 'Product status' based on the condition\n",
"df_snapshot['Industrialization'] = df_snapshot['Production Status'].apply(lambda x: 'Industrialized' if x.strip() == 'Industrialized' else 'Not Industrialized')\n",
"\n",
"# Create a pivot table with 'Product Category', 'Pty Indice', and 'Top-Level Status' as index\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index=['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice'],\n",
" values=['IDD Total Sales', 'IDD Total Marge'],\n",
" aggfunc='sum',\n",
" fill_value=0).reset_index()\n",
"\n",
"# Sort pivot_table_df by 'Top-Level Status', 'Industrialization', then 'Product Category'\n",
"sort_order = ['Clear-to-Build', 'Short']\n",
"pivot_table_df['Top-Level Status'] = pd.Categorical(pivot_table_df['Top-Level Status'], categories=sort_order, ordered=True)\n",
"pivot_table_df['Industrialization'] = pd.Categorical(pivot_table_df['Industrialization'], categories=['Industrialized', 'Not Industrialized'], ordered=True)\n",
"pivot_table_df.sort_values(by=['Top-Level Status', 'Industrialization', 'Product Category'], inplace=True)\n",
"\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 81\n",
"start_cell_col = 14 # Column 'N81'\n",
"\n",
"# Write headers for each column\n",
"headers = ['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice', 'IDD Total Sales', 'IDD Total Marge']\n",
"for c_idx, col in enumerate(headers, start=start_cell_col):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Initialize a set to track written statuses and categories\n",
"written_statuses_categories = set()\n",
"\n",
"# Write data rows\n",
"for r_idx, row in pivot_table_df.iterrows():\n",
" current_status = row['Top-Level Status']\n",
" current_industrialization = row['Industrialization']\n",
" current_category = row['Product Category']\n",
" \n",
" # Write Top-Level Status and Industrialization only if it's the first occurrence\n",
" if (current_status, current_industrialization) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col, value=current_status)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 1, value=current_industrialization)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization)) # Add current_status and current_industrialization to written_statuses\n",
" \n",
" # Write Product Category only if Status, Industrialization, and Category are first occurrence\n",
" if (current_status, current_industrialization, current_category) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 2, value=current_category)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization, current_category)) # Add (current_status, current_industrialization, current_category) to written_statuses\n",
" \n",
" # Write Pty Indice\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 3, value=row['Pty Indice'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write IDD Total Sales as currency\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4, value=row['IDD Total Sales'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4).number_format = '$#,##0.00'\n",
" \n",
" # Write IDD Total Marge as currency\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 5, value=row['IDD Total Marge'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 5).number_format = '$#,##0.00'\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Total Sales & IDD Marge per Pty Indice by Top-Level Status, Production Status & Product Category\\nInputs date: {file_date} - Source: |CM-Snapshot|\"\n",
"\n",
"# Create a bar chart\n",
"chart14 = BarChart()\n",
"chart14.title = chart_title\n",
"chart14.x_axis.title = None\n",
"chart14.y_axis.title = 'IDD Total Sales & Marge'\n",
"\n",
"###################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 4,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 5,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart14.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_col=start_cell_col + 3,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart14.set_categories(categories)\n",
"\n",
"############################\n",
"# Set the size of the chart\n",
"############################\n",
"chart14.width = 80 # Adjust the width as needed\n",
"chart14.height = 16 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart14, \"B116\")\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#15 []\n",
"##############################################################################################################################\n",
"# Merge df_backlog with df_snapshot to bring 'Top-Level Status' and 'Production Status' into df_backlog\n",
"df_backlog = pd.merge(df_backlog, df_snapshot[['Pty Indice', 'Top-Level Status', 'Production Status']], on='Pty Indice', how='left')\n",
"\n",
"# Define 'Order Type' column based on 'Order' column, excluding rows containing 'NC'\n",
"df_backlog['Order Type'] = df_backlog['Order'].apply(lambda x: 'DX/DO' if str(x).startswith('D') else ('Standard' if 'NC' not in str(x) else None))\n",
"\n",
"# Rename 'Production Status_x' to 'Production Status' if needed\n",
"if 'Production Status_x' in df_backlog.columns:\n",
" df_backlog.rename(columns={'Production Status_x': 'Production Status'}, inplace=True)\n",
"\n",
"# Rename 'Top-Level Status_x' to 'Top-Level Status' if needed\n",
"if 'Top-Level Status_x' in df_backlog.columns:\n",
" df_backlog.rename(columns={'Top-Level Status_x': 'Top-Level Status'}, inplace=True)\n",
" \n",
"display(df_backlog)\n",
"\n",
"# Create a new column 'Industrialization' based on the condition\n",
"df_backlog['Industrialization'] = df_backlog['Production Status'].apply(lambda x: 'Industrialized' if x.strip() == 'Industrialized' else 'Not Industrialized')\n",
"\n",
"# Create a pivot table with 'Top-Level Status', 'Industrialization', 'Order Type', and 'Pty Indice' as index\n",
"pivot_table_df = pd.pivot_table(df_backlog,\n",
" index=['Top-Level Status', 'Industrialization', 'Order Type', 'Pty Indice'],\n",
" values=['Backlog row Qty'],\n",
" aggfunc='sum',\n",
" fill_value=0).reset_index()\n",
"\n",
"# Sort pivot_table_df by 'Top-Level Status', 'Industrialization', then 'Order Type'\n",
"sort_order = ['Active', 'Inactive']\n",
"pivot_table_df['Top-Level Status'] = pd.Categorical(pivot_table_df['Top-Level Status'], categories=sort_order, ordered=True)\n",
"pivot_table_df['Industrialization'] = pd.Categorical(pivot_table_df['Industrialization'], categories=['Industrialized', 'Not Industrialized'], ordered=True)\n",
"pivot_table_df.sort_values(by=['Top-Level Status', 'Industrialization', 'Order Type'], inplace=True)\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 81\n",
"start_cell_col = 27 # Column 'AA81'\n",
"\n",
"# Write headers for each column\n",
"headers = ['Top-Level Status', 'Industrialization', 'Order Type', 'Pty Indice', 'Backlog row Qty']\n",
"for c_idx, col in enumerate(headers, start=start_cell_col):\n",
" cell = graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
"\n",
"# Initialize a set to track written statuses, industrializations, categories\n",
"written_statuses_categories = set()\n",
"\n",
"# Write data rows\n",
"for r_idx, row in pivot_table_df.iterrows():\n",
" current_status = row['Top-Level Status']\n",
" current_industrialization = row['Industrialization']\n",
" current_order_type = row['Order Type']\n",
"\n",
" # Write Top-Level Status, Industrialization, and Order Type only if it's the first occurrence\n",
" if (current_status, current_industrialization, current_order_type) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col, value=current_status)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 1, value=current_industrialization)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 2, value=current_order_type)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization, current_order_type)) # Add to written_statuses\n",
"\n",
" # Write Pty Indice\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 3, value=row['Pty Indice'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write Backlog row Qty\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4, value=row['Backlog row Qty'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Top-Level Status, Industrialization & Product Category\\nInputs date: {file_date} - Source: |CM-Snapshot|\"\n",
"\n",
"chart15 = BarChart()\n",
"chart15.title = chart_title\n",
"chart15.x_axis.title = None\n",
"chart15.y_axis.title = 'IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build'\n",
"\n",
"########################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 4,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 4,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart15.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_col=start_cell_col + 3,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart15.set_categories(categories)\n",
"\n",
"############################\n",
"# Set the size of the chart\n",
"############################\n",
"chart15.width = 80 # Adjust the width as needed\n",
"chart15.height = 16 # Adjust the height as needed\n",
"\n",
"# Positioning the chart\n",
"graph_sheet.add_chart(chart15, \"B151\")\n",
"\n",
"#*******************************************************************************************************************************************\n",
"############################################################\n",
"# Get the sheet view (there should be only one sheet view)\n",
"############################################################\n",
"sheet_view = graph_sheet.sheet_view\n",
"\n",
"# Set the zoom scale to 60% \n",
"sheet_view.zoomScale = 60\n",
"\n",
"# Save the workbook\n",
"workbook.save(output_file_name_Graph)\n",
"print(f\"Charts successfully added to '{output_file_name_Graph}'\")\n"
]
},
{
"cell_type": "markdown",
"id": "d0abe86a-0fbe-46de-bcff-1000a4cf9130",
"metadata": {
"jp-MarkdownHeadingCollapsed": true
},
"source": [
"<h2 style=\"text-align:left;\">Section |Dashboard| to insert on the code</h2> "
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "43f085d8-2f56-4e4a-b266-c2d2ff2694d3",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n",
"KeyboardInterrupt\n",
"\n"
]
}
],
"source": [
"#***************************************************************************************************************************\n",
"#############################################################################################################################\n",
"## ######## ######## ######### ######### ## ## \n",
"## ## ## ## ## ## ## ## ## ## \n",
"## ## #### ######## ######### ######### ######### \n",
"## ## ## ## ### ## ## ## ## ## \n",
"## ######## ## ## ## ## ## ## ## \n",
"##############################################################################################################################\n",
"#***************************************************************************************************************************\n",
"# Creating tab |Dashboard| in first position \n",
"#***************************************************************************************************************************\n",
"#########################################################################################################\n",
"# load the formatted workbook\n",
"######################################################################################################\n",
"# Load the workbook\n",
"try:\n",
" workbook = load_workbook(original_input)\n",
" #print(f\"Successfully loaded '{original_input}'\")\n",
"except FileNotFoundError as e:\n",
" print(f\"File not found: {e}\")\n",
" exit()\n",
" \n",
"# Print the sheet names\n",
"print(\"Tabs in the workbook:\")\n",
"print(workbook.sheetnames)\n",
"print('Processing |Dashboard|...')\n",
"\n",
"# Create a new \"Dashboard\" sheet as the first sheet\n",
"graph_sheet = workbook.create_sheet(title='Dashboard', index=0)\n",
"\n",
"# Check if 'Dashboard' sheet already exists and replace it\n",
"if 'Dashboard' in workbook.sheetnames:\n",
" # Delete existing 'Dashboard' sheet\n",
" workbook.remove(workbook['Dashboard'])\n",
"\n",
"# Create 'Dashboard' sheet\n",
"graph_sheet = workbook.create_sheet(title='Dashboard', index = 0)\n",
"print(\"|Dashboard| tab created successfully.\") \n",
"\n",
"######################################################################################################\n",
"# Define the relevant columns for \"Project snapshot\"\n",
"#####################################################################################################\n",
"#********************************************************************************************************************************\n",
"# Divide |Graph| in 2 section --> |Project History| |General Overview| |Clear to Build Overview| |Progression Overview| \n",
" # | Project Status |\n",
"#******************************************************************************************************************************\n",
"# Write title \"Project History\" in cell A1\n",
"graph_sheet['A1'] = 'Project History'\n",
"graph_sheet['A1'].font = Font(bold=True)\n",
"graph_sheet['A1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells A1 to N1\n",
"graph_sheet.merge_cells('A1:M1')\n",
"\n",
"# Write title \"General Overview\" in cell N1\n",
"graph_sheet['N1'] = 'General Overview'\n",
"graph_sheet['N1'].font = Font(bold=True)\n",
"graph_sheet['N1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells N1 to Z1\n",
"graph_sheet.merge_cells('N1:Z1')\n",
"\n",
"# Write title \"General Overview\" in cell AA1\n",
"graph_sheet['AA1'] = 'Clear to Build Overview'\n",
"graph_sheet['AA1'].font = Font(bold=True)\n",
"graph_sheet['AA1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells AA1 to AM1\n",
"graph_sheet.merge_cells('AA1:AM1')\n",
"\n",
"# Write title \"Progression Overview\" in cell AN1\n",
"graph_sheet['AN1'] = 'Progression Overview'\n",
"graph_sheet['AN1'].font = Font(bold=True)\n",
"graph_sheet['AN1'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells AN1 to A1:AZ1\n",
"graph_sheet.merge_cells('AN1:AZ1')\n",
"\n",
"# Write title \"Project Status\" in cell A79\n",
"graph_sheet['A79'] = 'Project Status'\n",
"graph_sheet['A79'].font = Font(bold=True)\n",
"graph_sheet['A79'].alignment = Alignment(horizontal='center')\n",
"\n",
"# Merge cells A79 to AZ79\n",
"graph_sheet.merge_cells('A79:AZ79')\n",
"\n",
"####################\n",
"#Formatting row 1\n",
"##################\n",
"# Define fill color (blue)\n",
"fill = PatternFill(start_color='8DB4E2', end_color='8DB4E2', fill_type='solid')\n",
"\n",
"# Font properties (white color, size 16)\n",
"font = Font(color='FFFFFF', size=16)\n",
"\n",
"# Apply styles to row 1\n",
"for col in range(1, graph_sheet.max_column + 1):\n",
" cell = graph_sheet.cell(row=1, column=col)\n",
" cell.fill = fill\n",
" cell.font = font\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
"\n",
"####################\n",
"#Formatting row 79\n",
"##################\n",
"# Apply styles to row 79\n",
"for col in range(1, graph_sheet.max_column + 1):\n",
" cell = graph_sheet.cell(row=79, column=col)\n",
" cell.fill = fill\n",
" cell.font = font\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" \n",
"###############################\n",
"# Graph Label number\n",
"###############################\n",
"'''\n",
"graph_sheet['A2'] = 1\n",
"graph_sheet['A27'] = 2\n",
"graph_sheet['A52'] = 3\n",
"graph_sheet['N2'] = 4\n",
"graph_sheet['N27'] = 5\n",
"graph_sheet['N52'] = 6\n",
"graph_sheet['AA2'] = 7\n",
"graph_sheet['AA27'] = 8\n",
"graph_sheet['AA52'] = 9\n",
"graph_sheet['AN2'] = 10\n",
"graph_sheet['AN27'] = 11\n",
"graph_sheet['AN52'] = 12\n",
"graph_sheet['A80'] = 13\n",
"graph_sheet['A115'] = 14\n",
"graph_sheet['A151'] = 15\n",
"'''\n",
"\n",
"# Write numbers in specified positions using indices\n",
"positions = [(2, 1, 1), (2, 14, 4), (2, 27, 7), (27, 1, 2), (27, 14, 5), (27, 27, 8), (2, 40, 10), (27, 40, 11), (52, 1, 3), (52, 14, 6), (52, 27, 9), (52, 40, 12), (80, 1, 13), (115, 1, 14), (151, 1, 15)] # (row, column, 'Number')\n",
"for row_idx, col_idx, number in positions:\n",
" cell = graph_sheet.cell(row=row_idx, column=col_idx)\n",
" cell.value = number\n",
" cell.fill = fill\n",
" cell.font = Font(color='FFFFFF', size=14, bold=True)\n",
" cell.alignment = Alignment(horizontal='center', vertical='center')\n",
" \n",
"#########################################################################\n",
"# Set the width of column A to 2.5 (or any desired width)\n",
"graph_sheet.column_dimensions['A'].width = 4\n",
"\n",
"# Set the width of column N to 2 (or any desired width)\n",
"graph_sheet.column_dimensions['N'].width = 4\n",
"\n",
"# Set the width of column AA to 2 (or any desired width)\n",
"graph_sheet.column_dimensions['AA'].width = 4\n",
"\n",
"# Set the width of column AN to 2 (or any desired width)\n",
"graph_sheet.column_dimensions['AN'].width = 4\n",
"\n",
"##########################################################################################\n",
"## Horizontal tick border column N, AB, AM, AZ/ Vertical dashDot border A30:AZ30, A52:AZ52\n",
"##########################################################################################\n",
"# Define a thick border style for the upper border\n",
"thick_border_upper = Border(top=Side(style='thick',color='000000'))\n",
"\n",
"# Apply the thick border to row 2 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=2, column=col)\n",
" cell.border = thick_border_upper\n",
"\n",
"# Define a dashdot border style for the upper border\n",
"dash_border_upper = Border(top=Side(style='dashDot',color='808080'))\n",
"\n",
"# Apply the Dash border to row 27 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=27, column=col)\n",
" cell.border = dash_border_upper\n",
"\n",
"# Apply the Dash border to row 52 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=52, column=col)\n",
" cell.border = dash_border_upper\n",
"\n",
"#####################################\n",
"### Define vertical thick border style\n",
"##########################################\n",
"thick_border_left = Border(left=Side(style='thick'))\n",
"\n",
"# Apply the thick border to column N from row 1 to 79\n",
"for row in range(1, 80):\n",
" cell_N = graph_sheet.cell(row=row, column=14) # Column N is the 14th column\n",
" cell_N.border = thick_border_left\n",
"\n",
"# Apply the thick border to column AA from row 1 to 79\n",
"for row in range(1, 80):\n",
" cell_AA = graph_sheet.cell(row=row, column=27) # Column AA is the 27th column\n",
" cell_AA.border = thick_border_left\n",
"\n",
"# Apply the thick border to column AM from row 1 to 79\n",
"for row in range(1, 80):\n",
" cell_AM = graph_sheet.cell(row=row, column=40) # Column AM is the 40th column\n",
" cell_AM.border = thick_border_left\n",
"\n",
"# Apply the thick border to column AZ from row 1 to 150\n",
"for row in range(1, 149):\n",
" cell_AM = graph_sheet.cell(row=row, column=53) # Column AZ is the 53th column\n",
" cell_AM.border = thick_border_left\n",
"\n",
"############################\n",
"#Combined Left/Upper border \n",
"############################\n",
"# Apply both the thick left and upper borders to cell N27\n",
"cell_N2 = graph_sheet.cell(row=2, column=14) # Column N is the 14th column\n",
"cell_N2.border = Border(left=thick_border_left.left, top=thick_border_upper.top)\n",
"\n",
"# Apply both the thick left and DashDot upper borders to cell AA27\n",
"cell_AA27= graph_sheet.cell(row=27, column=27) # Column AA is the 27th column\n",
"cell_AA27.border = Border(left=thick_border_left.left, top=dash_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell N27\n",
"cell_N27 = graph_sheet.cell(row=27, column=14) # Column N is the 14th column\n",
"cell_N27.border = Border(left=thick_border_left.left, top=dash_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell AA2\n",
"cell_AA2 = graph_sheet.cell(row=2, column=27) # Column AA is the 28th column\n",
"cell_AA2.border = Border(left=thick_border_left.left, top=thick_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell AN27\n",
"cell_AN27 = graph_sheet.cell(row=27, column=40) # Column N is the 14th column\n",
"cell_AN27.border = Border(left=thick_border_left.left, top=dash_border_upper.top)\n",
"\n",
"# Apply both the thick left and upper borders to cell AN2\n",
"cell_AN2 = graph_sheet.cell(row=2, column=40) # Column AN is the 28th column\n",
"cell_AN2.border = Border(left=thick_border_left.left, top=thick_border_upper.top)\n",
"\n",
"################################################\n",
"# Horizantal Border for section |Project Status|\n",
"###############################################\n",
"# Apply the thick border to row 79 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=79, column=col)\n",
" cell.border = thick_border_upper\n",
"\n",
"# Apply the thick border to row 80 from column A to AZ\n",
"for col in range(1, 53): # Columns A to AZ (1 to 53)\n",
" cell = graph_sheet.cell(row=80, column=col)\n",
" cell.border = thick_border_upper\n",
" \n",
"###################################\n",
"#****************************************************************************************************************************\n",
"# CHARTS \n",
"#****************************************************************************************************************************\n",
"#####################################################\n",
"#Create a new dataframe including the completed PN\n",
"#######################################################\n",
"#Include the Pty Indice not in |Snapshot| (df_Snapshot) but present in |CM-Priority| (df_Priority) with 'Production Status' = 'Completed' \n",
"#Include in a new df_Snapshot_Priority the column 'Description','Production Status', 'IDD Sale Price', 'SEDA Sale Price' from CM-Priority\n",
"df_Priority_filtered = df_Priority[df_Priority['Production Status'] == 'Completed'][['Pty Indice', 'Priority', 'IDD Top Level', 'SEDA Top Level', 'Shipped', 'Remain. crit. Qty','Description', 'Production Status', 'SEDA Sale Price']]\n",
"\n",
"#Merge df_snapshot with df_Priority_filtered based on 'Pty Indice'\n",
"df_snapshot_priority = pd.concat([df_snapshot, df_Priority_filtered], ignore_index=True)\n",
"\n",
"# Fill NaN values in 'Top-Level Status' with 'Completed' for the newly merged rows\n",
"df_snapshot_priority['Top-Level Status'].fillna('Completed', inplace=True)\n",
"\n",
"#Aplly function to fill the 'Product Category' \n",
"def determine_category(description):\n",
" if not isinstance(description, str):\n",
" return 'Others'\n",
" if description == 'Rototellite':\n",
" return 'Rototellite'\n",
" elif 'Indicator' in description or 'CPA' in description:\n",
" return 'CPA'\n",
" elif 'Lightplate' in description:\n",
" return 'Lightplate'\n",
" elif 'ISP' in description or 'Keyboard' in description:\n",
" return 'ISP'\n",
" elif 'Module' in description:\n",
" return 'CPA'\n",
" elif 'optics' in description:\n",
" return 'Fiber Optics'\n",
" else:\n",
" return 'Others'\n",
"\n",
"# Apply the determine_category function to 'Description' column\n",
"df_snapshot_priority['Product Category'] = df_snapshot_priority['Description'].apply(determine_category)\n",
"\n",
"#display(df_snapshot_priority)\n",
"\n",
"####################################################################################\n",
"#***************************************************************************************************************************\n",
"#|Project History|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#1 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"category_counts = df_snapshot_priority['Product Category'].value_counts(normalize=True) * 100\n",
"category_percentages = category_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 3, column 2 (B3)\n",
"graph_sheet.cell(row=3, column=2, value=\"Product Category\")\n",
"graph_sheet.cell(row=3, column=3, value=\"Percentage\")\n",
"\n",
"# Write category and percentage data to the worksheet\n",
"for idx, (category, percentage) in enumerate(category_percentages.items(), start=4):\n",
" graph_sheet.cell(row=idx, column=2, value=category)\n",
" graph_sheet.cell(row=idx, column=3, value=percentage)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Product Categories\\nInputs date: {file_date_inventory} - Source: |CM-Priority|\"\n",
"\n",
"# Create a pie chart\n",
"chart1 = PieChart()\n",
"chart1.title = chart_title\n",
"\n",
"################################################\n",
"# Prepare data for the pie chart using Reference\n",
"####################################################\n",
"min_row = 3 # Start from row 3 where your data starts\n",
"max_row = min_row + len(category_percentages) # Calculate the last row based on the number of categories\n",
"min_row_label = 4 # Start from row 4 for labels\n",
"max_row_label = min_row_label + len(category_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=3, min_row=min_row, max_row=max_row) # Adjusted min_col to 3\n",
"labels = Reference(graph_sheet, min_col=2, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 2\n",
"\n",
"chart1.add_data(data_points, titles_from_data=True)\n",
"chart1.set_categories(labels)\n",
" \n",
"#############################\n",
"# Set size/position\n",
"##############################\n",
"chart1.width = 20 # Adjust the width as needed\n",
"chart1.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning \n",
"graph_sheet.add_chart(chart1, \"B3\")\n",
"\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#2 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"status_counts = df_snapshot_priority['Production Status'].value_counts(normalize=True) * 100\n",
"status_percentages = status_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 28, column 2 (B28)\n",
"graph_sheet.cell(row=28, column=2, value=\"Production Status\")\n",
"graph_sheet.cell(row=28, column=3, value=\"Percentage\")\n",
"\n",
"# Write category and percentage data to the worksheet\n",
"for idx, (status, percentage) in enumerate(status_percentages.items(), start=28): # Start index corrected to 28\n",
" graph_sheet.cell(row=idx + 1, column=2, value=status) # Adjusted row index and added +1 to start from 29\n",
" graph_sheet.cell(row=idx + 1, column=3, value=percentage) # Adjusted row index and added +1 to start from 29\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Production Status\\nInputs date: {file_date_inventory} - Source: |CM-Priority|\"\n",
"\n",
"# Create a pie chart\n",
"chart2 = PieChart()\n",
"chart2.title = chart_title\n",
"################################################\n",
"\n",
"# Prepare data for the pie chart using Reference\n",
"min_row = 28 # Start from row 28 to include the data\n",
"max_row = min_row + len(status_percentages) # Calculate the last row based on the number of statuses\n",
"min_row_label = 29 # Start from row 29 for labels\n",
"max_row_label = min_row_label + len(status_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=3, min_row=min_row, max_row=max_row) # Adjusted min_col to 3\n",
"labels = Reference(graph_sheet, min_col=2, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 2\n",
"\n",
"chart2.add_data(data_points, titles_from_data=True)\n",
"chart2.set_categories(labels)\n",
"\n",
"# Set the size of the chart\n",
"chart2.width = 20 # Adjust the width as needed\n",
"chart2.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart\n",
"graph_sheet.add_chart(chart2, \"B28\")\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#3 []\n",
"##############################################################################################################################\n",
"\n",
"\n",
"\n",
"#***************************************************************************************************************************\n",
"#|General Overview|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#4 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"category_counts = df_snapshot['Product Category'].value_counts(normalize=True) * 100\n",
"category_percentages = category_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 3, column 15 (O3)\n",
"graph_sheet.cell(row=3, column=15, value=\"Product Category\")\n",
"graph_sheet.cell(row=3, column=16, value=\"Percentage\")\n",
"\n",
"# Write category and percentage data to the worksheet\n",
"for idx, (category, percentage) in enumerate(category_percentages.items(), start=4):\n",
" graph_sheet.cell(row=idx, column=15, value=category)\n",
" graph_sheet.cell(row=idx, column=16, value=percentage)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Product Categories\\nInputs date: {file_date_inventory} - Source: |CM-Snapshot|\"\n",
"\n",
"# Create a pie chart\n",
"chart4 = PieChart()\n",
"chart4.title = chart_title\n",
"\n",
"##########################################################\n",
"# Prepare data for the pie chart using Reference\n",
"min_row = 3 # Start from row 3 where your data starts\n",
"max_row = min_row + len(category_percentages) # Calculate the last row based on the number of categories\n",
"min_row_label = 4 # Start from row 4 for labels\n",
"max_row_label = min_row_label + len(category_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=16, min_row=min_row, max_row=max_row) # Adjusted min_col to 16\n",
"labels = Reference(graph_sheet, min_col=15, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 15\n",
"\n",
"chart4.add_data(data_points, titles_from_data=True)\n",
"chart4.set_categories(labels)\n",
" \n",
"#############################\n",
"# Set the size of the chart4\n",
"##############################\n",
"chart4.width = 20 # Adjust the width as needed\n",
"chart4.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart4 \n",
"graph_sheet.add_chart(chart4, \"O3\")\n",
"##############################################################################################################################\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#5 []\n",
"##############################################################################################################################\n",
"# Calculate percentage distribution and round to one decimal place\n",
"status_counts = df_snapshot['Production Status'].value_counts(normalize=True) * 100\n",
"status_percentages = status_counts.round(1)\n",
"\n",
"# Write data to the worksheet starting from row 28, column 15 (O28)\n",
"graph_sheet.cell(row=28, column=15, value=\"Production Status\")\n",
"graph_sheet.cell(row=28, column=16, value=\"Percentage\")\n",
"\n",
"# Write status and percentage data to the worksheet starting from row 29\n",
"for idx, (status, percentage) in enumerate(status_percentages.items(), start=29):\n",
" graph_sheet.cell(row=idx, column=15, value=status)\n",
" graph_sheet.cell(row=idx, column=16, value=percentage)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Percentage Distribution of Production Status\\nInputs date: {file_date_inventory} - Source: |CM-Snapshot|\"\n",
"\n",
"chart5 = PieChart()\n",
"chart5.title = chart_title\n",
"\n",
"############################################################\n",
"# Prepare data for the pie chart using Reference\n",
"min_row = 28 # Start from row 29 to include the data\n",
"max_row = min_row + len(status_percentages) # Calculate the last row based on the number of statuses\n",
"min_row_label = 29 # Start from row 29 for labels\n",
"max_row_label = min_row_label + len(status_percentages) - 1\n",
"\n",
"data_points = Reference(graph_sheet, min_col=16, min_row=min_row, max_row=max_row) # Adjusted min_col to 16\n",
"labels = Reference(graph_sheet, min_col=15, min_row=min_row_label, max_row=max_row_label) # Adjusted min_col to 15\n",
"\n",
"chart5.add_data(data_points, titles_from_data=True)\n",
"chart5.set_categories(labels)\n",
" \n",
"#############################\n",
"# Set size/position\n",
"##############################\n",
"chart5.width = 20 # Adjust the width as needed\n",
"chart5.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning \n",
"graph_sheet.add_chart(chart5, \"O28\")\n",
"\n",
"##############################################################################################################################\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#6 []\n",
"##############################################################################################################################\n",
"\n",
"\n",
"#***************************************************************************************************************************\n",
"#|Clear to Build Overview|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#7 []\n",
"##############################################################################################################################\n",
"# Calculate unique IDD Top Level for each Product Category and each classification\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index='Product Category',\n",
" columns='Top-Level Status',\n",
" values='IDD Top Level',\n",
" aggfunc=pd.Series.nunique,\n",
" fill_value=0)\n",
"\n",
"# Calculate total unique IDD Top Level across all categories\n",
"total_unique_idd_top_level = df_snapshot['IDD Top Level'].nunique()\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 3\n",
"start_cell_col = 28 # Column 'AB3'\n",
"\n",
"# Write headers\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col, value=\"Product Category\")\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 1, value=\"Unique IDD Top Level (Clear-to-Build)\")\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 2, value=\"Unique IDD Top Level (Short)\")\n",
"\n",
"# Write data rows\n",
"for r_idx, (index, row) in enumerate(pivot_table_df.iterrows(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=index) # Write Product Category\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 1, value=row['Clear-to-Build']) # Write unique count for Clear-to-Build\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 2, value=row['Short']) # Write unique count for Short\n",
"\n",
"# Add the total unique IDD Top Level label and value in the last row\n",
"total_row = start_cell_row + len(pivot_table_df) + 1\n",
"graph_sheet.cell(row=total_row, column=start_cell_col, value=\"Total Unique IDD Top Level\")\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 1, value=pivot_table_df['Clear-to-Build'].sum())\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 2, value=pivot_table_df['Short'].sum())\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Count of Unique IDD Top Level by Product Category\\nInputs date: {file_date_inventory} - Source: |Snapshot|\"\n",
"\n",
"# Create a bar chart (chart6)\n",
"chart7 = BarChart()\n",
"chart7.title = chart_title\n",
"chart7.x_axis.title = 'Product Category'\n",
"chart7.y_axis.title = 'Count of Unique IDD Top Level'\n",
"\n",
"##################################################\n",
"# Define data for the chart (exclude totals row)\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 2,\n",
" max_row=start_cell_row + len(pivot_table_df) + 1)\n",
"chart7.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_row=start_cell_row + len(pivot_table_df) + 1)\n",
"chart7.set_categories(categories)\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart7.width = 20 # Adjust the width as needed\n",
"chart7.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart7, \"AB3\")\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#8 []\n",
"##############################################################################################################################\n",
"# Create a pivot table with 'Product Category' as index\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index='Product Category',\n",
" columns='Top-Level Status',\n",
" values='IDD Backlog Qty',\n",
" aggfunc='sum',\n",
" fill_value=0)\n",
"\n",
"# Calculate unique IDD Top Level for Clear to Build and Short\n",
"unique_idd_top_level_ctb = df_snapshot[df_snapshot['Top-Level Status'] == 'Clear-to-Build']['IDD Top Level'].nunique()\n",
"unique_idd_top_level_short = df_snapshot[df_snapshot['Top-Level Status'] == 'Short']['IDD Top Level'].nunique()\n",
"\n",
"#print('Unique IDD Top Level for Clear to Build:', unique_idd_top_level_ctb)\n",
"#print('Unique IDD Top Level for Short:', unique_idd_top_level_short)\n",
"\n",
"# Write the pivot table to the graph_sheet starting from AB28\n",
"start_cell_row = 28\n",
"start_cell_col = 28 # Column 'AB28'\n",
"\n",
"# Write headers for each column first\n",
"for c_idx, col in enumerate(pivot_table_df.columns, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Write data rows\n",
"for r_idx, (index, row) in enumerate(pivot_table_df.iterrows(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=index) # Write Product Category\n",
" for c_idx, value in enumerate(row, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"# Add the total unique IDD Top Level labels and values in the second row\n",
"total_row = start_cell_row + 1\n",
"graph_sheet.cell(row=total_row, column=start_cell_col, value=\"Total unique IDD Top Level\")\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 1, value=unique_idd_top_level_ctb)\n",
"graph_sheet.cell(row=total_row, column=start_cell_col + 2, value=unique_idd_top_level_short)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Backlog Qty by Product Category and Top-Level Status\\nInputs date: {file_date_inventory} - Source: |Snapshot|\"\n",
"\n",
"chart8 = BarChart()\n",
"chart8.title = chart_title\n",
"chart8.x_axis.title = 'Product Category'\n",
"chart8.y_axis.title = 'IDD Backlog Qty'\n",
"\n",
"##################################################\n",
"# Define data for the chart including total unique IDD Top Level and all rows\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row, \n",
" max_col=start_cell_col + len(pivot_table_df.columns),\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"chart8.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis) including the total unique IDD Top Level label and all rows\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1, \n",
" max_row=start_cell_row + len(pivot_table_df)) \n",
"chart8.set_categories(categories)\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart8.width = 20 # Adjust the width as needed\n",
"chart8.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart8, \"AB28\")\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#9 []\n",
"##############################################################################################################################\n",
"# Merge df_backlog with df_snapshot to bring 'Top-Level Status' into df_backlog\n",
"df_backlog = pd.merge(df_backlog, df_snapshot[['Pty Indice', 'Top-Level Status']], on='Pty Indice', how='left')\n",
"\n",
"# Define 'Order Type' column based on 'Order' column, excluding rows containing 'NC'\n",
"df_backlog['Order Type'] = df_backlog['Order'].apply(lambda x: 'DX/DO' if str(x).startswith('D') else ('Standard' if 'NC' not in str(x) else None))\n",
"\n",
"# Calculate sum of 'Backlog row Qty' by 'Pty Indice' and 'Order Type'\n",
"#df_backlog['Sum Backlog Qty'] = df_backlog.groupby(['Pty Indice', 'Order Type'])['Backlog row Qty'].transform('sum')\n",
"\n",
"#display(df_backlog) \n",
"\n",
"\n",
"# Calculate sum of 'Backlog row Qty' separately for 'Standard' and 'DX/DO'\n",
"sum_standard = df_backlog.loc[df_backlog['Order Type'] == 'Standard', 'Backlog row Qty'].sum()\n",
"sum_dx_do = df_backlog.loc[df_backlog['Order Type'] == 'DX/DO', 'Backlog row Qty'].sum()\n",
"\n",
"#print(f\"Sum of 'Backlog row Qty' for 'Standard': {sum_standard}\")\n",
"#print(f\"Sum of 'Backlog row Qty' for 'DX/DO': {sum_dx_do}\")\n",
"\n",
"#Create pivot table\n",
"pivot_table_df = pd.pivot_table(df_backlog,\n",
" index='Order Type',\n",
" columns='Top-Level Status',\n",
" values='Backlog row Qty',\n",
" aggfunc='sum',\n",
" fill_value=0)\n",
"\n",
"# Write the pivot table to the graph_sheet starting from AB53\n",
"start_cell_row = 53\n",
"start_cell_col = 28 # Column AB\n",
"\n",
"# Write headers for each column first\n",
"for c_idx, col in enumerate(pivot_table_df.columns, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Write data rows\n",
"for r_idx, (index, row) in enumerate(pivot_table_df.iterrows(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=index) # Write Order Type\n",
" for c_idx, value in enumerate(row, start=start_cell_col + 1):\n",
" graph_sheet.cell(row=r_idx, column=c_idx, value=value)\n",
"\n",
"##################################\n",
"#Create and configure the chart\n",
"######################################\n",
"chart_title = f\"IDD Backlog Qty by type of order\\nInputs date: {file_date_inventory} - Source: |CM-Backlog|\"\n",
"\n",
"chart9 = BarChart()\n",
"chart9.title = chart_title\n",
"chart9.x_axis.title = 'Order Type'\n",
"chart9.y_axis.title = 'IDD Backlog Qty'\n",
"########################################\n",
"\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row, \n",
" max_col=start_cell_col + len(pivot_table_df.columns),\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"chart9.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1, \n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"chart9.set_categories(categories)\n",
"\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart9.width = 20 # Adjust the width as needed\n",
"chart9.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart9, \"AB53\")\n",
"\n",
"#***************************************************************************************************************************\n",
"#|Progression Overview|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#10 []\n",
"##############################################################################################################################\n",
"# Filter to exclude rows where 'Order' contains 'NC'\n",
"filtered_df = df_TurnoverReport[~df_TurnoverReport['Order'].str.contains('NC')]\n",
"\n",
"# Define the span period of the report\n",
"start_date = filtered_df['Invoice date'].min()\n",
"end_date = filtered_df['Invoice date'].max()\n",
"span_period = f\"{start_date} to {end_date}\"\n",
"\n",
"# Group by 'Pty Indice' and sum 'TurnoverReport row Qty'\n",
"sum_qty_by_indice = filtered_df.groupby('Pty Indice')['TurnoverReport row Qty'].sum()\n",
"\n",
"# Print or display the result\n",
"#print(sum_qty_by_indice)\n",
"\n",
"# Write sum_qty_by_indice to Excel starting from cell AO3\n",
"start_cell_row = 3\n",
"start_cell_col = 41 # Column 'AO'\n",
"\n",
"# Write headers\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col, value='Pty Indice')\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 1, value='Qty shipped')\n",
"\n",
"# Write data rows\n",
"for r_idx, (indice, sum_qty) in enumerate(sum_qty_by_indice.items(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=indice)\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 1, value=sum_qty)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Qty shipped by Pty Indice - {span_period}\\nInputs date: {file_date_inventory} - Source: |CM-TurnoverReport|\"\n",
"\n",
"chart10 = BarChart()\n",
"chart10.title = chart_title #f'Qty shipped by Pty Indice - {span_period}'\n",
"chart10.x_axis.title = 'Pty Indice'\n",
"chart10.y_axis.title = 'Qty shipped'\n",
"\n",
"###################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart10.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart10.set_categories(categories)\n",
"\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart10.width = 20 # Adjust the width as needed\n",
"chart10.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart10, \"AO3\")\n",
"\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#11 []\n",
"##############################################################################################################################\n",
"# Filter to include only rows where 'Order' contains 'NC'\n",
"filtered_df = df_TurnoverReport[df_TurnoverReport['Order'].str.contains('NC')].copy()\n",
"\n",
"# Categorize 'TurnoverReport row Qty' as 'Shipped' or 'Received'\n",
"filtered_df['Category'] = filtered_df['TurnoverReport row Qty'].apply(lambda x: 'Shipped' if x > 0 else 'Received')\n",
"\n",
"\n",
"# Adjust negative values of 'TurnoverReport row Qty' to positive\n",
"filtered_df['TurnoverReport row Qty'] = filtered_df['TurnoverReport row Qty'].abs()\n",
"\n",
"# Group by 'Pty Indice' and sum 'TurnoverReport row Qty'\n",
"sum_qty_by_indice = filtered_df.groupby('Pty Indice')['TurnoverReport row Qty'].sum()\n",
"\n",
"#display(sum_qty_by_indice)\n",
"\n",
"# Create a pivot table with 'Pty Indice' as index and 'Category' as columns\n",
"pivot_table_df = pd.pivot_table(filtered_df,\n",
" index='Pty Indice',\n",
" columns='Category',\n",
" values='TurnoverReport row Qty',\n",
" aggfunc='sum',\n",
" fill_value=0)\n",
"\n",
"# Write sum_qty_by_indice to Excel starting from cell AO28\n",
"start_cell_row = 28\n",
"start_cell_col = 41 # Column 'AO'\n",
"\n",
"# Write headers\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col, value='Pty Indice')\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 1, value='Qty shipped')\n",
"graph_sheet.cell(row=start_cell_row, column=start_cell_col + 2, value='Category')\n",
"\n",
"# Write data rows\n",
"for r_idx, (indice, sum_qty) in enumerate(sum_qty_by_indice.items(), start=start_cell_row + 1):\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col, value=indice)\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 1, value=sum_qty)\n",
" # Assigning the category based on sum_qty\n",
" category = 'Shipped' if sum_qty > 0 else 'Received'\n",
" graph_sheet.cell(row=r_idx, column=start_cell_col + 2, value=category)\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"Qty shipped by Pty Indice - {span_period}\\nInputs date: {file_date_inventory} - Source: |CM-TurnoverReport|\"\n",
"\n",
"chart11 = BarChart()\n",
"chart11.title = chart_title #f'Qty shipped by Pty Indice - {span_period}'\n",
"chart11.x_axis.title = 'Pty Indice'\n",
"chart11.y_axis.title = 'Qty shipped'\n",
"\n",
"#################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 1,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart11.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_row=start_cell_row + len(sum_qty_by_indice))\n",
"chart11.set_categories(categories)\n",
"\n",
"###############################\n",
"# Set the size of the chart\n",
"###############################\n",
"chart11.width = 20 # Adjust the width as needed\n",
"chart11.height = 11 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart11, \"AO28\")\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#12 []\n",
"##############################################################################################################################\n",
"\n",
"\n",
"\n",
"\n",
"#***************************************************************************************************************************\n",
"#|Project Status|\n",
"#***************************************************************************************************************************\n",
"##############################################################################################################################\n",
"# Creating Graph#13 [] - IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Top-Level Status, Production Status & Product Category\n",
"##############################################################################################################################\n",
"# Create a new column 'Product status' based on the condition\n",
"df_snapshot['Industrialization'] = df_snapshot['Production Status'].apply(lambda x: 'Industrialized' if x.strip() == 'Industrialized' else 'Not Industrialized')\n",
"\n",
"# Create a pivot table with 'Product Category', 'Pty Indice', and 'Top-Level Status' as index\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index=['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice'],\n",
" values=['IDD Backlog Qty', 'Remain. crit. Qty', 'Qty clear to build'],\n",
" aggfunc='sum',\n",
" fill_value=0).reset_index()\n",
"\n",
"# Sort pivot_table_df by 'Top-Level Status', 'Industrialization', then 'Product Category'\n",
"sort_order = ['Clear-to-Build', 'Short']\n",
"pivot_table_df['Top-Level Status'] = pd.Categorical(pivot_table_df['Top-Level Status'], categories=sort_order, ordered=True)\n",
"pivot_table_df['Industrialization'] = pd.Categorical(pivot_table_df['Industrialization'], categories=['Industrialized', 'Not Industrialized'], ordered=True)\n",
"pivot_table_df.sort_values(by=['Top-Level Status', 'Industrialization', 'Product Category'], inplace=True)\n",
"\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 81\n",
"start_cell_col = 2 # Column 'B81'\n",
"\n",
"# Write headers for each column\n",
"headers = ['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice', 'IDD Backlog Qty', 'Remain. crit. Qty', 'Qty clear to build']\n",
"for c_idx, col in enumerate(headers, start=start_cell_col):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Initialize a set to track written statuses and categories\n",
"written_statuses_categories = set()\n",
"\n",
"# Write data rows\n",
"for r_idx, row in pivot_table_df.iterrows():\n",
" current_status = row['Top-Level Status']\n",
" current_industrialization = row['Industrialization']\n",
" current_category = row['Product Category']\n",
" \n",
" # Write Top-Level Status and Industrialization only if it's the first occurrence\n",
" if (current_status, current_industrialization) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col, value=current_status)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 1, value=current_industrialization)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization)) # Add current_status and current_industrialization to written_statuses\n",
" \n",
" # Write Product Category only if Status, Industrialization, and Category are first occurrence\n",
" if (current_status, current_industrialization, current_category) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 2, value=current_category)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization, current_category)) # Add (current_status, current_industrialization, current_category) to written_statuses\n",
" \n",
" # Write Pty Indice\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 3, value=row['Pty Indice'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write IDD Backlog Qty\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4, value=row['IDD Backlog Qty'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write Remain. crit. Qty \n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 5, value=row['Remain. crit. Qty'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write Qty clear to build\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 6, value=row['Qty clear to build'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Top-Level Status, Production Status & Product Category\\nInputs date: {file_date_inventory} - Source: |CM-Snapshot|\"\n",
"\n",
"chart13 = BarChart()\n",
"chart13.title = \"IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Top-Level Status, Production Status & Product Category\"\n",
"chart13.x_axis.title = None\n",
"chart13.y_axis.title = 'IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build '\n",
"\n",
"########################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 4,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 6,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart13.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_col=start_cell_col + 3,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart13.set_categories(categories)\n",
"\n",
"############################\n",
"# Set the size of the chart\n",
"############################\n",
"chart13.width = 80 # Adjust the width as needed\n",
"chart13.height = 16 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart13, \"B81\")\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#14 - IDD Total Sales & IDD Marge per Pty Indice by Top-Level Status, Production Status & Product Categor\n",
"##############################################################################################################################\n",
"# Calculate IDD Total Sales\n",
"df_snapshot['IDD Total Sales'] = df_snapshot['IDD Backlog Qty'] * df_snapshot['IDD Sale Price']\n",
"\n",
"# Calculate IDD Total Marge\n",
"df_snapshot['IDD Total Marge'] = df_snapshot['IDD Backlog Qty'] * df_snapshot['IDD Marge Standard (unit)']\n",
"\n",
"# Create a new column 'Product status' based on the condition\n",
"df_snapshot['Industrialization'] = df_snapshot['Production Status'].apply(lambda x: 'Industrialized' if x.strip() == 'Industrialized' else 'Not Industrialized')\n",
"\n",
"# Create a pivot table with 'Product Category', 'Pty Indice', and 'Top-Level Status' as index\n",
"pivot_table_df = pd.pivot_table(df_snapshot,\n",
" index=['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice'],\n",
" values=['IDD Total Sales', 'IDD Total Marge'],\n",
" aggfunc='sum',\n",
" fill_value=0).reset_index()\n",
"\n",
"# Sort pivot_table_df by 'Top-Level Status', 'Industrialization', then 'Product Category'\n",
"sort_order = ['Clear-to-Build', 'Short']\n",
"pivot_table_df['Top-Level Status'] = pd.Categorical(pivot_table_df['Top-Level Status'], categories=sort_order, ordered=True)\n",
"pivot_table_df['Industrialization'] = pd.Categorical(pivot_table_df['Industrialization'], categories=['Industrialized', 'Not Industrialized'], ordered=True)\n",
"pivot_table_df.sort_values(by=['Top-Level Status', 'Industrialization', 'Product Category'], inplace=True)\n",
"\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 81\n",
"start_cell_col = 14 # Column 'N81'\n",
"\n",
"# Write headers for each column\n",
"headers = ['Top-Level Status', 'Industrialization', 'Product Category', 'Pty Indice', 'IDD Total Sales', 'IDD Total Marge']\n",
"for c_idx, col in enumerate(headers, start=start_cell_col):\n",
" graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
"\n",
"# Initialize a set to track written statuses and categories\n",
"written_statuses_categories = set()\n",
"\n",
"# Write data rows\n",
"for r_idx, row in pivot_table_df.iterrows():\n",
" current_status = row['Top-Level Status']\n",
" current_industrialization = row['Industrialization']\n",
" current_category = row['Product Category']\n",
" \n",
" # Write Top-Level Status and Industrialization only if it's the first occurrence\n",
" if (current_status, current_industrialization) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col, value=current_status)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 1, value=current_industrialization)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization)) # Add current_status and current_industrialization to written_statuses\n",
" \n",
" # Write Product Category only if Status, Industrialization, and Category are first occurrence\n",
" if (current_status, current_industrialization, current_category) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 2, value=current_category)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization, current_category)) # Add (current_status, current_industrialization, current_category) to written_statuses\n",
" \n",
" # Write Pty Indice\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 3, value=row['Pty Indice'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write IDD Total Sales as currency\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4, value=row['IDD Total Sales'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4).number_format = '$#,##0.00'\n",
" \n",
" # Write IDD Total Marge as currency\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 5, value=row['IDD Total Marge'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 5).number_format = '$#,##0.00'\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Total Sales & IDD Marge per Pty Indice by Top-Level Status, Production Status & Product Category\\nInputs date: {file_date_inventory} - Source: |CM-Snapshot|\"\n",
"\n",
"# Create a bar chart\n",
"chart14 = BarChart()\n",
"chart14.title = chart_title\n",
"chart14.x_axis.title = None\n",
"chart14.y_axis.title = 'IDD Total Sales & Marge'\n",
"\n",
"###################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 4,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 5,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart14.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_col=start_cell_col + 3,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart14.set_categories(categories)\n",
"\n",
"############################\n",
"# Set the size of the chart\n",
"############################\n",
"chart14.width = 80 # Adjust the width as needed\n",
"chart14.height = 16 # Adjust the height as needed\n",
"\n",
"# Positioning the chart \n",
"graph_sheet.add_chart(chart14, \"B116\")\n",
"\n",
"\n",
"##############################################################################################################################\n",
"# Creating Graph#15 - IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Industrialization & Type of order\n",
"##############################################################################################################################\n",
"# Merge df_backlog with df_snapshot to bring 'Top-Level Status' and 'Production Status' into df_backlog\n",
"df_backlog = pd.merge(df_backlog, df_snapshot[['Pty Indice', 'Top-Level Status', 'Production Status']], on='Pty Indice', how='left')\n",
"\n",
"# Define 'Order Type' column based on 'Order' column, excluding rows containing 'NC'\n",
"df_backlog['Order Type'] = df_backlog['Order'].apply(lambda x: 'DX/DO' if str(x).startswith('D') else ('Standard' if 'NC' not in str(x) else None))\n",
"\n",
"# Rename 'Production Status_x' to 'Production Status' if needed\n",
"if 'Production Status_x' in df_backlog.columns:\n",
" df_backlog.rename(columns={'Production Status_x': 'Production Status'}, inplace=True)\n",
"\n",
"# Rename 'Top-Level Status_x' to 'Top-Level Status' if needed\n",
"if 'Top-Level Status_x' in df_backlog.columns:\n",
" df_backlog.rename(columns={'Top-Level Status_x': 'Top-Level Status'}, inplace=True)\n",
" \n",
"#display(df_backlog)\n",
"\n",
"# Create a new column 'Industrialization' based on the condition\n",
"df_backlog['Industrialization'] = df_backlog['Production Status'].apply(lambda x: 'Industrialized' if x.strip() == 'Industrialized' else 'Not Industrialized')\n",
"\n",
"# Create a pivot table with 'Top-Level Status', 'Industrialization', 'Order Type', and 'Pty Indice' as index\n",
"pivot_table_df = pd.pivot_table(df_backlog,\n",
" index=['Top-Level Status', 'Industrialization', 'Order Type', 'Pty Indice'],\n",
" values=['Backlog row Qty'],\n",
" aggfunc='sum',\n",
" fill_value=0).reset_index()\n",
"\n",
"# Sort pivot_table_df by 'Top-Level Status', 'Industrialization', then 'Order Type'\n",
"sort_order = ['Active', 'Inactive']\n",
"pivot_table_df['Top-Level Status'] = pd.Categorical(pivot_table_df['Top-Level Status'], categories=sort_order, ordered=True)\n",
"pivot_table_df['Industrialization'] = pd.Categorical(pivot_table_df['Industrialization'], categories=['Industrialized', 'Not Industrialized'], ordered=True)\n",
"pivot_table_df.sort_values(by=['Top-Level Status', 'Industrialization', 'Order Type'], inplace=True)\n",
"\n",
"# Write headers for each column first\n",
"start_cell_row = 81\n",
"start_cell_col = 27 # Column 'AA81'\n",
"\n",
"# Write headers for each column\n",
"headers = ['Top-Level Status', 'Industrialization', 'Order Type', 'Pty Indice', 'Backlog row Qty']\n",
"for c_idx, col in enumerate(headers, start=start_cell_col):\n",
" cell = graph_sheet.cell(row=start_cell_row, column=c_idx, value=col)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
"\n",
"# Initialize a set to track written statuses, industrializations, categories\n",
"written_statuses_categories = set()\n",
"\n",
"# Write data rows\n",
"for r_idx, row in pivot_table_df.iterrows():\n",
" current_status = row['Top-Level Status']\n",
" current_industrialization = row['Industrialization']\n",
" current_order_type = row['Order Type']\n",
"\n",
" # Write Top-Level Status, Industrialization, and Order Type only if it's the first occurrence\n",
" if (current_status, current_industrialization, current_order_type) not in written_statuses_categories:\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col, value=current_status)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 1, value=current_industrialization)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 2, value=current_order_type)\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" written_statuses_categories.add((current_status, current_industrialization, current_order_type)) # Add to written_statuses\n",
"\n",
" # Write Pty Indice\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 3, value=row['Pty Indice'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
" \n",
" # Write Backlog row Qty\n",
" cell = graph_sheet.cell(row=r_idx + start_cell_row + 1, column=start_cell_col + 4, value=row['Backlog row Qty'])\n",
" cell.font = Font(color=\"FFFFFF\") # Set font color to white\n",
"\n",
"###############################################\n",
"# Create the chart title including the subtitle\n",
"################################################\n",
"chart_title = f\"IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build per Pty Indice by Industrialization & Type of order\\nInputs date: {file_date_inventory} - Source: |CM-Snapshot|\"\n",
"\n",
"chart15 = BarChart()\n",
"chart15.title = chart_title\n",
"chart15.x_axis.title = None\n",
"chart15.y_axis.title = 'IDD Backlog Qty, Remain. crit. Qty & Qty clear-to-build'\n",
"\n",
"########################################################\n",
"# Define data for the chart\n",
"data = Reference(graph_sheet,\n",
" min_col=start_cell_col + 4,\n",
" min_row=start_cell_row,\n",
" max_col=start_cell_col + 4,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart15.add_data(data, titles_from_data=True)\n",
"\n",
"# Set categories (x-axis)\n",
"categories = Reference(graph_sheet,\n",
" min_col=start_cell_col,\n",
" min_row=start_cell_row + 1,\n",
" max_col=start_cell_col + 3,\n",
" max_row=start_cell_row + len(pivot_table_df))\n",
"\n",
"chart15.set_categories(categories)\n",
"\n",
"############################\n",
"# Set the size of the chart\n",
"############################\n",
"chart15.width = 80 # Adjust the width as needed\n",
"chart15.height = 16 # Adjust the height as needed\n",
"\n",
"# Positioning the chart\n",
"graph_sheet.add_chart(chart15, \"B152\")\n",
"\n",
"#***************************************************************************************************************************\n",
"# Formating |Dashboard| \n",
"#***************************************************************************************************************************\n",
"############################################################\n",
"# Get the sheet view (there should be only one sheet view)\n",
"############################################################\n",
"sheet_view = graph_sheet.sheet_view\n",
"\n",
"# Set the zoom scale to 60% \n",
"sheet_view.zoomScale = 60\n",
" \n",
"###################################################################################################################\n",
"# Final save and selection of |Dashboard| as active tab\n",
"###################################################################################################################\n",
"\n",
"# Save the updated workbook\n",
"workbook.save(original_input)\n",
"workbook.close()\n",
"print(f\"Dashboard added successfully as |Dashboard| in {original_input}\")\n",
"\n",
"###################################################################################################################\n",
"#Coloring tab |Dashboard|Snapshot|Summary|Clear-to-Build|CM-Inventory|CM-BOM|CM-Priority|CM-Backlog|\n",
"###################################################################################################################\n",
"##### load the formatted workbook\n",
"workbook = load_workbook(original_input) \n",
"\n",
"################################\n",
"# Move |Gantt| in position 7 \n",
"################################\n",
"# Function to move a sheet to a specific position\n",
"def move_sheet_to_position(workbook, sheet_name, position):\n",
" sheet_names = workbook.sheetnames\n",
" sheet_names.remove(sheet_name)\n",
" sheet_names.insert(position, sheet_name)\n",
" workbook._sheets = [workbook[n] for n in sheet_names]\n",
"\n",
"# Move 'Gantt' to position 7 (index 6, since indexing starts at 0)\n",
"move_sheet_to_position(workbook, 'Gantt', 6)\n",
"\n",
"workbook.save(original_input)\n",
"\n",
"#########################"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2bfcfcb5-df81-4bdc-9d96-249c4fc9ea14",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}