currently we are taking week number from the image name . is it possible to extract from the image.
for example in one unmatched line : Unmatched line in Mahalakshmi_Week01_2025.PNG: Week 1 2025, commencing Saturday 28/12/2021: this week)
you can see 'Week 1 2025'. Could you modify the program to save this week number into excel file instead of week number from image name
import os
import re
import threading
from tkinter import (
    Tk, Label, Button, filedialog, messagebox, Text, Scrollbar, Entry, StringVar,
    END, DISABLED, NORMAL, Frame, LabelFrame, Menu
)
from tkinter.ttk import Progressbar, Style
from PIL import Image, ImageEnhance, ImageFilter
import pytesseract
import pandas as pd

# ---------- CONFIG ----------
pytesseract.pytesseract.tesseract_cmd = r"C:\Users\36083\timesheet_env\Tesseract-OCR\tesseract.exe"
custom_config  = r'--oem 3 --psm 6 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ '

line_pattern = re.compile(
    r'(.+?)\s+([0-9O]{1,2})\s+([0-9O]{1,2})\s+([0-9O]{1,2})\s+([0-9O]{1,2})\s+([0-9O]{1,2})\s+([0-9O]{1,2})\s+([0-9O]{1,2})\s+([0-9O]{1,2})\b'
)

def preprocess_image(img):
    img = img.convert("L")
    img = img.resize((img.width * 2, img.height * 2), Image.LANCZOS)
    img = ImageEnhance.Contrast(img).enhance(2)
    img = img.filter(ImageFilter.SHARPEN)
    return img

def classify_project(raw):
    compact = re.sub(r'\s+', '', raw.strip(), flags=re.I)
    if re.search(r'annualholiday', compact, re.I):
        return "Annual Holiday"
    if re.search(r'absence', compact, re.I):
        return "ODEC Absence"
    return "Project"

def parse_weeks(week_str):
    """Parse a string like '12,13,15-17' into a set of week numbers."""
    weeks = set()
    for part in week_str.split(','):
        part = part.strip()
        if '-' in part:
            start, end = part.split('-')
            weeks.update(range(int(start), int(end)+1))
        elif part.isdigit():
            weeks.add(int(part))
    return weeks

def process_timesheets(image_folder, output_excel, log_callback, progress_callback, week_filter=None):
    data = []
    all_files = []
    for root, dirs, files in os.walk(image_folder):
        for filename in files:
            if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
                all_files.append((root, filename))
    total_files = len(all_files)
    processed_files = 0

    for root, filename in all_files:
        image_path = os.path.join(root, filename)
        # Parse name & week from filename
        name, week = "Unknown", "Unknown"
        file_match = re.match(r"(.+?)_Week(\d+)_\d+\.(png|jpg|jpeg)$", filename, re.IGNORECASE)
        if file_match:
            name, week = file_match.group(1), file_match.group(2)
        else:
            name = filename.rsplit('.', 1)[0]

        # Filter by week if needed
        if week_filter and week != "Unknown":
            if int(week) not in week_filter:
                processed_files += 1
                progress_callback(processed_files, total_files)
                continue

        try:
            img  = Image.open(image_path)
            img  = preprocess_image(img)
            text = pytesseract.image_to_string(img, config=custom_config).strip()
            text = text.encode('ascii', errors='ignore').decode()
            log_callback(f"OCR output for {filename}:\n{text}\n")
            if not text:
                log_callback(f"No text found in image: {filename}\n")
                processed_files += 1
                progress_callback(processed_files, total_files)
                continue
        except Exception as e:
            log_callback(f"Error reading image {filename}: {e}\n")
            processed_files += 1
            progress_callback(processed_files, total_files)
            continue

        matched = False
        for line in text.splitlines():
            match = line_pattern.search(line)
            if not match:
                if line.strip():
                    log_callback(f"Unmatched line in {filename}: {line.strip()}\n")
                continue

            original_project = match.group(1).strip()
            if original_project.lower().startswith("total"):
                continue

            # Clean digits
            days = [match.group(i).replace('O', '0') for i in range(2, 9)]
            total = match.group(9).replace('O', '0')

            # Validate
            try:
                days  = [int(d) for d in days]
                total = int(total)
            except ValueError:
                log_callback(f"Skipping malformed line in {filename}: {line}\n")
                continue

            project = classify_project(original_project)

            data.append({
                "Name": name,
                "Week": week,
                "Project": project,
                "Sat": days[0],
                "Sun": days[1],
                "Mon": days[2],
                "Tue": days[3],
                "Wed": days[4],
                "Thu": days[5],
                "Fri": days[6],
                "Total": total
            })
            matched = True

        if not matched:
            log_callback(f"No valid timesheet lines found in {filename}\n")
        processed_files += 1
        progress_callback(processed_files, total_files)

    if data:
        df = pd.DataFrame(data)

        # Separate "Project" and non-"Project" rows
        project_rows = df[df["Project"] == "Project"]
        other_rows = df[df["Project"] != "Project"]

        # Group and sum "Project" rows by Name and Week
        if not project_rows.empty:
            summed = (
                project_rows
                .groupby(["Name", "Week"], as_index=False)[["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"]]
                .sum()
            )
            # Recompute Total as sum of Sat-Fri
            summed["Total"] = summed[["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"]].sum(axis=1)
            summed["Project"] = "Project"
            # Reorder columns to match original
            summed = summed[["Name", "Week", "Project", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Total"]]
        else:
            summed = pd.DataFrame(columns=["Name", "Week", "Project", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Total"])

        # Combine summed "Project" rows with other rows
        final_df = pd.concat([summed, other_rows], ignore_index=True)
        # Optional: sort for neatness
        final_df = final_df.sort_values(["Name", "Week", "Project"])

        final_df.to_excel(output_excel, index=False)
        return True, output_excel
    else:
        return False, None

# ---------- GUI ----------
class TimesheetGUI:
    def __init__(self, master):
        self.master = master
        master.title("Timesheet Extractor")
        master.geometry("800x600")
        master.minsize(700, 500)

        # Set a modern theme
        style = Style()
        style.theme_use('clam')

        # Menu bar
        menubar = Menu(master)
        helpmenu = Menu(menubar, tearoff=0)
        helpmenu.add_command(label="About", command=self.show_about)
        helpmenu.add_command(label="Help", command=self.show_help)
        menubar.add_cascade(label="Help", menu=helpmenu)
        master.config(menu=menubar)

        # Top Frame for title
        top_frame = Frame(master, bg="#e6f2ff")
        top_frame.pack(fill="x", padx=10, pady=5)
        Label(top_frame, text="Timesheet Extractor", font=("Arial", 18, "bold"), bg="#e6f2ff").pack(side="left", padx=10)

        # Input Frame
        input_frame = LabelFrame(master, text="Input", padx=10, pady=10)
        input_frame.pack(fill="x", padx=10, pady=5)

        Label(input_frame, text="Select the input folder containing timesheet images:").pack(anchor="w")
        self.select_button = Button(input_frame, text="Browse Folder", command=self.browse_folder)
        self.select_button.pack(anchor="w", pady=2)
        self.folder_label = Label(input_frame, text="", fg="blue")
        self.folder_label.pack(anchor="w", pady=2)

        Label(input_frame, text="Week Filter:").pack(anchor="w")
        self.week_var = StringVar()
        self.week_entry = Entry(input_frame, textvariable=self.week_var, width=40)
        self.week_entry.pack(anchor="w", pady=2)
        self.week_entry.insert(0, "Optional: Enter week(s) e.g. 12,13,15-17")

        self.submit_button = Button(input_frame, text="Submit", command=self.run_extraction, state="disabled")
        self.submit_button.pack(anchor="w", pady=5)

        # Progress Frame
        progress_frame = Frame(master)
        progress_frame.pack(fill="x", padx=10, pady=5)
        self.progress = Progressbar(progress_frame, orient="horizontal", length=400, mode="determinate")
        self.progress.pack(side="left", padx=5, pady=5)
        self.progress_label = Label(progress_frame, text="0%")
        self.progress_label.pack(side="left", padx=5)

        # Log Frame
        log_frame = LabelFrame(master, text="Log", padx=10, pady=10)
        log_frame.pack(fill="both", expand=True, padx=10, pady=5)
        self.log_text = Text(log_frame, height=12, width=90, state=DISABLED, wrap="word")
        self.log_text.pack(side="left", fill="both", expand=True)
        self.scrollbar = Scrollbar(log_frame, command=self.log_text.yview)
        self.log_text['yscrollcommand'] = self.scrollbar.set
        self.scrollbar.pack(side="right", fill="y")
        self.clear_log_button = Button(log_frame, text="Clear Log", command=self.clear_log)
        self.clear_log_button.pack(anchor="ne", padx=5, pady=2)

        # Status Bar
        self.status = Label(master, text="Ready", bd=1, relief="sunken", anchor="w", bg="#f0f0f0")
        self.status.pack(side="bottom", fill="x")

        self.input_folder = ""
        self.output_excel = ""

    def browse_folder(self):
        folder_selected = filedialog.askdirectory()
        if folder_selected:
            self.input_folder = folder_selected
            self.folder_label.config(text=self.input_folder)
            self.output_excel = os.path.join(
                self.input_folder,
                "Extracted_Timesheet.xlsx"
            )
            self.submit_button.config(state="normal")
            self.status.config(text="Folder selected. Ready to process.")

    def log(self, message):
        self.log_text.config(state=NORMAL)
        self.log_text.insert(END, message)
        self.log_text.see(END)
        self.log_text.config(state=DISABLED)

    def clear_log(self):
        self.log_text.config(state=NORMAL)
        self.log_text.delete(1.0, END)
        self.log_text.config(state=DISABLED)

    def update_progress(self, value, maximum):
        self.progress["maximum"] = maximum
        self.progress["value"] = value
        percent = int((value / maximum) * 100) if maximum else 0
        self.progress_label.config(text=f"{percent}%")
        self.status.config(text=f"Processing... {value} of {maximum} files")
        self.master.update_idletasks()

    def run_extraction(self):
        if not self.input_folder or not os.path.isdir(self.input_folder):
            messagebox.showerror("Error", "Please select a valid input folder.")
            return
        self.submit_button.config(state="disabled")
        self.status.config(text="Processing, please wait...")
        self.progress["value"] = 0
        self.progress_label.config(text="0%")
        self.clear_log()
        threading.Thread(target=self._run_extraction_thread).start()

    def _run_extraction_thread(self):
        week_filter = None
        week_str = self.week_var.get().strip()
        if week_str and not week_str.lower().startswith("optional"):
            try:
                week_filter = parse_weeks(week_str)
            except Exception as e:
                self.log(f"Invalid week filter: {e}\n")
                week_filter = None

        success, output_path = process_timesheets(
            self.input_folder,
            self.output_excel,
            self.log,
            self.update_progress,
            week_filter
        )
        if success:
            self.status.config(text="Done! Data written to Excel.")
            messagebox.showinfo("Done", f"Timesheet data successfully written to:\n{output_path}")
        else:
            self.status.config(text="No data extracted.")
            messagebox.showwarning("No Data", "No timesheet data extracted from any image.")
        self.submit_button.config(state="normal")
        self.progress["value"] = 0
        self.progress_label.config(text="0%")

    def show_about(self):
        messagebox.showinfo("About", "Timesheet Extractor\nVersion 1.0\n\nDeveloped by Your Team.")

    def show_help(self):
        messagebox.showinfo(
            "Help",
            "1. Click 'Browse Folder' to select the folder with timesheet images.\n"
            "2. (Optional) Enter week numbers to filter (e.g., 12,13,15-17).\n"
            "3. Click 'Submit' to start extraction.\n"
            "4. Progress and logs will be shown below.\n"
            "5. The result Excel file will be saved in the selected folder."
        )

if __name__ == "__main__":
    root = Tk()
    gui = TimesheetGUI(root)
    root.mainloop()