diff --git a/webster123-v1.0.2.py b/webster123-v1.0.2.py
new file mode 100644
index 0000000..c4199e4
--- /dev/null
+++ b/webster123-v1.0.2.py
@@ -0,0 +1,743 @@
+import tkinter as tk
+from tkinter import font, filedialog, messagebox, simpledialog, Toplevel, Text, Scrollbar, Menu
+import pandas as pd
+import os
+import webbrowser
+
+global_df = None # Global variable to store the DataFrame
+current_selection = None
+clipboard_data = None
+shift_start_row = None # Variable to store the starting row for shift-click selection
+
+# Define the column order
+columns = [
+ "siteId", "folderPath", "ourUrl", "ourTitle", "ourContent", "Extra1", "Extra2","topMenu",
+ "ourHeader", "ourFooter", "styleSheet", "scriptsUrl", "fileExtension", "ourMeta", "shareImageUrl",
+ "Website", "websiteUrl","Icon", "topHtml", "headTag", "ourShareButton", "useLinkBox", "directoryMode", "frontPage"
+]
+# Define initial values
+initial_data = {
+ "ourUrl": ["publish-and-view-me"],
+ "folderPath": ["c:\\webster123"],
+ "fileExtension": ["html"],
+ "scriptsUrl": [""],
+ "ourTitle": [""],
+ "ourContent": [f"""
Congratulations!
You've published your first page.
Change it to your html to get started.
This is Webster123 v1.0.2
Visit Webster123.com for instructions.
"""],
+ "Extra1": [""],
+ "Extra2": [""],
+ "siteId": ["My Site"],
+ "topMenu": [""],
+ "ourHeader": [f""""""],
+ "ourFooter": [""],
+ "directoryMode": ["False"],
+ "shareImageUrl": [""],
+ "ourMeta": [""],
+ "Website": [""],
+ "websiteUrl": [""],
+ "styleSheet": [""],
+ "Icon": [""],
+ "topHtml": [""],
+ "headTag": [""],
+ "ourShareButton": [""],
+ "useLinkBox": ["False"],
+ "frontPage": ["False"]
+}
+
+# Add 20 extra empty rows
+for _ in range(20):
+ for key in initial_data.keys():
+ initial_data[key].append("")
+
+column_config = {
+ "ourUrl": {"width": 220, "chars": 24, "instructions": "Words with a dash between them, no special characters"},
+ "folderPath": {"width": 100, "chars": 12, "instructions": "The folder on your local where you wish to store the pages you create. Like C:\\webster123"},
+ "fileExtension": {"width": 100, "chars": 12, "instructions": "html or php"},
+ "ourTitle": {"width": 100, "chars": 12, "instructions": "The title of your web page."},
+ "ourContent": {"width": 100, "chars": 12, "instructions": "Html content"},
+ "Extra1": {"width": 100, "chars": 12, "instructions": "Extra Html content"},
+ "Extra2": {"width": 100, "chars": 12, "instructions": "Extra Html content"},
+ "siteId": {"width": 100, "chars": 12, "instructions": "Your site Id, Which site is this?"},
+ "topMenu": {"width": 100, "chars": 12, "instructions": "Our menu entries are Anchor links stacked on top of each other."},
+ "ourHeader": {"width": 100, "chars": 12, "instructions": "Html for the header of the website."},
+ "ourFooter": {"width": 100, "chars": 12, "instructions": "Html for the Footer of our site."},
+ "directoryMode": {"width": 100, "chars": 12, "instructions": "False and we produce a url like example.html. True and we create a folder example/ and put an index page in it.."},
+ "shareImageUrl": {"width": 100, "chars": 12, "instructions": "The url of your share image"},
+ "ourMeta": {"width": 100, "chars": 12, "instructions": "The meta Description of your page."},
+ "Website": {"width": 100, "chars": 12, "instructions": "yoursite.com"},
+ "websiteUrl": {"width": 100, "chars": 12, "instructions": "Website URL. Must have trailing slash '/', like https://yoursite.com/"},
+ "styleSheet": {"width": 100, "chars": 12, "instructions": "The url of your stylesheet file. On your local drive it can look like file:///c:/Stylesheets/mystylesheet.css This way you can work with a stylesheet on your drive. When you publish the page on the internet, you can change it to something like https://mysite.com/mystylesheet.css"},
+ "Icon": {"width": 100, "chars": 12, "instructions": "The website icon, usually 100x100px"},
+ "topHtml": {"width": 100, "chars": 12, "instructions": "Inserted after "},
+ "headTag": {"width": 100, "chars": 12, "instructions": "Inserted after "},
+ "ourShareButton": {"width": 100, "chars": 12, "instructions": "AddtoAny Share Button. Leave blank to not use."},
+ "useLinkBox": {"width": 100, "chars": 12, "instructions": "If True, a Link To This Page Box will be added"},
+ "scriptsUrl": {"width": 100, "chars": 12, "instructions": "The url of your java script file. On your local drive it can look like file:///c:/Scriptsfolder/myscript.js This way you can work with a script on your drive. When you publish the page on the internet, you can change it to something like https://mysite.com/myscript.js"}
+
+}
+
+class SimpleTable(tk.Canvas):
+ def __init__(self, parent, rows=10, cols=5, font_size=10):
+ super().__init__(parent)
+ self.parent = parent
+ self.rows = rows
+ self.cols = cols
+ self.cells = {}
+ self.headers = []
+ self.row_numbers = []
+ self.header_height = 30
+ self.row_number_width = 50
+ self.font_size = font_size
+ self.font = font.Font(size=font_size)
+ self.cell_height = self.font.metrics("linespace") + 10
+
+ self.selection_rects = []
+ self.start_row = None
+ self.edit_window = None # Track the edit window
+ self.clipboard_data = None # To store single cell value for pasting
+
+ self.header_canvas = tk.Canvas(parent, height=self.header_height, bg='lightgrey')
+ self.row_number_canvas = tk.Canvas(parent, width=self.row_number_width, bg='lightgrey')
+
+ self.header_canvas.grid(row=0, column=1, sticky='ew')
+ self.row_number_canvas.grid(row=1, column=0, sticky='ns')
+
+ self.create_widgets()
+ self.bind_shortcuts()
+
+ def create_widgets(self):
+ self.config(bg='white', highlightthickness=0)
+ self.bind("", self.on_table_click)
+ self.bind("", self.on_table_drag)
+ self.bind("", self.on_drag_end)
+ self.bind("", self.on_table_double_click)
+ self.bind("", self.on_table_right_click)
+ self.bind_all("", self.on_mouse_wheel)
+ self.row_number_canvas.bind("", self.on_row_number_right_click)
+ self.xview_moveto(0)
+ self.yview_moveto(0)
+
+ def bind_shortcuts(self):
+ self.parent.bind("", self.copy_selection)
+ self.parent.bind("", self.cut_selection)
+ self.parent.bind("", self.paste_selection)
+
+ def cut_selection(self):
+ self.copy_selection()
+ self.delete_selection(current_selection[0], current_selection[1])
+
+ def create_cell(self, row, col, value):
+ col_name = self.headers[col]
+ cell_config = column_config.get(col_name, {"width": 100, "chars": 12})
+ cell_width = cell_config["width"]
+ x0 = self.row_number_width + sum(column_config.get(self.headers[c], {"width": 100})["width"] for c in range(col))
+ y0 = row * self.cell_height
+ x1 = x0 + cell_width
+ y1 = y0 + self.cell_height
+ rect = self.create_rectangle(x0, y0, x1, y1, fill="white", outline="black")
+
+ # Calculate the number of characters that fit in the cell width
+ char_width = self.font.measure("A")
+ max_chars = cell_width // char_width
+ display_value = str(value)[:max_chars] if value else ""
+
+ text = self.create_text(x0 + 5, y0 + 5, anchor="nw", text=display_value, width=cell_width - 10, font=self.font, tag=f"cell_{row}_{col}")
+ self.cells[(row, col)] = (rect, text)
+
+ def create_headers(self):
+ self.header_canvas.delete("all")
+ x_offset = 0
+ for col, header in enumerate(self.headers):
+ cell_config = column_config.get(header, {"width": 100, "chars": 12})
+ cell_width = cell_config["width"]
+ x0 = x_offset
+ y0 = 0
+ x1 = x0 + cell_width
+ y1 = y0 + self.header_height
+ self.header_canvas.create_rectangle(x0, y0, x1, y1, fill="lightgrey", outline="black")
+ self.header_canvas.create_text(x0 + 5, y0 + 5, anchor="nw", text=header, font=self.font)
+ x_offset += cell_width
+
+ def create_row_numbers(self):
+ self.row_number_canvas.delete("all")
+ for row in range(self.rows):
+ x0 = 0
+ y0 = row * self.cell_height
+ x1 = x0 + self.row_number_width
+ y1 = y0 + self.cell_height
+ rect = self.row_number_canvas.create_rectangle(x0, y0, x1, y1, fill="lightgrey", outline="black")
+ text = self.row_number_canvas.create_text(x0 + 5, y0 + 5, anchor="nw", text=str(row + 1), font=self.font)
+ self.row_numbers.append((rect, text))
+ self.row_number_canvas.tag_bind(rect, "", lambda event, r=row: self.on_row_number_click(event, r))
+ self.row_number_canvas.tag_bind(text, "", lambda event, r=row: self.on_row_number_click(event, r))
+
+ def on_row_number_click(self, event, row):
+ global shift_start_row
+ if shift_start_row is None or not event.state & 0x1: # If shift key is not pressed
+ shift_start_row = row
+ self.clear_selection()
+ self.highlight_rows(shift_start_row, row)
+ global current_selection
+ current_selection = (min(shift_start_row, row), 0, max(shift_start_row, row), self.cols - 1)
+
+ def highlight_rows(self, start_row, end_row):
+ for row in range(min(start_row, end_row), max(start_row, end_row) + 1):
+ for col in range(self.cols):
+ self.highlight_cell(row, col)
+
+ def load_data(self, data, column_names):
+ self.rows = len(data)
+ self.cols = len(data[0]) if self.rows > 0 else 0
+ self.headers = column_names
+ self.cells.clear()
+ self.delete("all")
+ self.populate_table()
+ for row, row_data in enumerate(data):
+ for col, value in enumerate(row_data):
+ self.create_cell(row, col, value)
+ self.update_idletasks()
+ self.config(scrollregion=self.bbox("all"))
+ self.create_headers()
+ self.create_row_numbers()
+ self.update_scroll_region()
+
+ def populate_table(self):
+ for row in range(self.rows):
+ for col in range(self.cols):
+ self.create_cell(row, col, "")
+ self.create_headers()
+ self.create_row_numbers()
+
+ def on_table_click(self, event):
+ global current_selection
+ x, y = self.canvasx(event.x), self.canvasy(event.y)
+ row, col = int(y // self.cell_height), self.get_col_at_x(x - self.row_number_width)
+ if row >= 0 and col >= 0:
+ self.clear_selection()
+ self.highlight_cell(row, col)
+ current_selection = (row, col, row, col)
+
+ def get_col_at_x(self, x):
+ x_offset = 0
+ for col, header in enumerate(self.headers):
+ cell_config = column_config.get(header, {"width": 100, "chars": 12})
+ cell_width = cell_config["width"]
+ if x_offset <= x < x_offset + cell_width:
+ return col
+ x_offset += cell_width
+ return -1
+
+ def on_table_drag(self, event):
+ global current_selection
+ x, y = self.canvasx(event.x), self.canvasy(event.y)
+ row, col = int(y // self.cell_height), self.get_col_at_x(x - self.row_number_width)
+ if row >= 0 and col >= 0 and current_selection is not None:
+ self.clear_selection()
+ row1, col1, row2, col2 = current_selection
+ current_selection = (row1, col1, row, col)
+ self.highlight_rectangle(row1, col1, row, col)
+
+ def on_drag_end(self, event):
+ self.start_row = None
+
+ def on_table_double_click(self, event):
+ x, y = self.canvasx(event.x), self.canvasy(event.y)
+ row, col = int(y // self.cell_height), self.get_col_at_x(x - self.row_number_width)
+ if row >= 0 and col >= 0:
+ self.edit_cell(row, col)
+
+ def on_table_right_click(self, event):
+ x, y = self.canvasx(event.x), self.canvasy(event.y)
+ row, col = int(y // self.cell_height), self.get_col_at_x(x - self.row_number_width)
+ if row >= 0 and col >= 0:
+ self.show_cell_context_menu(event, row, col)
+
+ def on_row_number_right_click(self, event):
+ y = self.row_number_canvas.canvasy(event.y)
+ row = int(y // self.cell_height)
+ if row >= 0:
+ self.show_row_context_menu(event, row)
+
+ def on_mouse_wheel(self, event):
+ if event.state & 0x1: # If Shift key is pressed
+ self.xview_scroll(-1 * int((event.delta / 120)), "units")
+ else:
+ self.yview_scroll(-1 * int((event.delta / 120)), "units")
+ self.update_scroll_region()
+
+ def highlight_cell(self, row, col):
+ self.itemconfig(self.cells[(row, col)][0], fill="lightblue")
+ self.itemconfig(self.cells[(row, col)][1], fill="blue", font="Arial 10 bold")
+
+ def clear_selection(self):
+ for rect in self.selection_rects:
+ self.delete(rect)
+ self.selection_rects.clear()
+ for (row, col), (rect, text) in self.cells.items():
+ self.itemconfig(rect, fill="white")
+ self.itemconfig(text, fill="black", font="Arial 10")
+
+ def highlight_rectangle(self, row1, col1, row2, col2):
+ for row in range(min(row1, row2), max(row1, row2) + 1):
+ for col in range(min(col1, col2), max(col1, col2) + 1):
+ self.highlight_cell(row, col)
+ cell_config = column_config.get(self.headers[col], {"width": 100, "chars": 12})
+ cell_width = cell_config["width"]
+ x0 = self.row_number_width + sum(column_config.get(self.headers[c], {"width": 100})["width"] for c in range(col))
+ y0 = row * self.cell_height
+ x1 = x0 + cell_width
+ y1 = y0 + self.cell_height
+ rect = self.create_rectangle(x0, y0, x1, y1, outline="blue", width=2)
+ self.selection_rects.append(rect)
+
+ def show_cell_context_menu(self, event, row, col):
+ menu = Menu(self, tearoff=0)
+ menu.add_command(label="Copy Selection", command=self.copy_selection)
+ menu.add_command(label="Paste Selection", command=self.paste_selection)
+ menu.add_command(label="Delete Selection", command=lambda: self.delete_selection(row, col))
+ menu.post(event.x_root, event.y_root)
+
+ def show_row_context_menu(self, event, row):
+ menu = Menu(self, tearoff=0)
+ menu.add_command(label="Insert Row", command=lambda: self.insert_row(row))
+ menu.add_command(label="Delete Row", command=lambda: self.delete_row(row))
+ menu.add_command(label="Copy Selection", command=self.copy_selection)
+ menu.add_command(label="Publish Selected Rows", command=self.publish_selected_rows)
+ menu.add_command(label="View Selected Rows", command=self.view_selected_rows)
+ menu.post(event.x_root, event.y_root)
+
+ def insert_row(self, row):
+ global global_df
+ new_row = [""] * self.cols
+ global_df = pd.concat([global_df.iloc[:row], pd.DataFrame([new_row], columns=global_df.columns), global_df.iloc[row:]]).reset_index(drop=True)
+ update_table()
+
+ def delete_row(self, row):
+ global global_df
+ global_df = global_df.drop(global_df.index[row]).reset_index(drop=True)
+ update_table()
+
+ def delete_selection(self, row, col):
+ global global_df, current_selection
+ if current_selection is not None:
+ row1, col1, row2, col2 = current_selection
+ global_df.iloc[min(row1, row2):max(row1, row2) + 1, min(col1, col2):max(col1, col2) + 1] = ""
+ update_table()
+
+ def copy_selection(self):
+ global global_df, clipboard_data, current_selection
+ if current_selection is not None:
+ row1, col1, row2, col2 = current_selection
+ if row1 == row2 and col1 == col2:
+ # Copy a single cell
+ clipboard_data = global_df.iat[row1, col1]
+ else:
+ # Copy a range of cells
+ clipboard_data = global_df.iloc[min(row1, row2):max(row1, row2) + 1, min(col1, col2):max(col1, col2) + 1].copy()
+
+ def paste_selection(self):
+ global global_df, clipboard_data, current_selection
+ if clipboard_data is not None and current_selection is not None:
+ row1, col1, row2, col2 = current_selection
+ if isinstance(clipboard_data, pd.DataFrame):
+ rows_to_paste = clipboard_data.shape[0]
+ cols_to_paste = clipboard_data.shape[1]
+ if row1 + rows_to_paste > self.rows or col1 + cols_to_paste > self.cols:
+ messagebox.showerror("Paste Error", "Not enough space to paste the selection")
+ return
+ for i in range(rows_to_paste):
+ for j in range(cols_to_paste):
+ global_df.iat[row1 + i, col1 + j] = clipboard_data.iat[i, j]
+ else:
+ for row in range(min(row1, row2), max(row1, row2) + 1):
+ for col in range(min(col1, col2), max(col1, col2) + 1):
+ global_df.iat[row, col] = clipboard_data
+ update_table()
+
+ def edit_cell(self, row, col):
+ if self.edit_window is not None and self.edit_window.winfo_exists():
+ self.edit_window.lift() # Bring the existing edit window to the front
+ return
+ if (row, col) not in self.cells:
+ return
+ full_text = global_df.iat[row, col] if not pd.isna(global_df.iat[row, col]) else "" # Handle NaN values
+ col_name = self.headers[col]
+ cell_config = column_config.get(col_name, {"width": 100, "chars": 12, "instructions": ""})
+ instructions = cell_config.get("instructions", "")
+
+ self.edit_window = tk.Toplevel(self)
+ self.edit_window.title(f"Edit Cell ({row}, {col})")
+
+ if instructions:
+ instruction_label = tk.Label(self.edit_window, text=instructions, wraplength=400, justify="left")
+ instruction_label.pack(fill="x", padx=10, pady=10)
+
+ text_frame = tk.Frame(self.edit_window, padx=10, pady=10)
+ text_frame.pack(fill="both", expand=True)
+
+ text = Text(text_frame, wrap="word")
+ text.insert("1.0", full_text)
+ text.pack(fill="both", expand=True, side="left")
+
+ scrollbar = Scrollbar(text_frame, command=text.yview)
+ scrollbar.pack(side="right", fill="y")
+ text.config(yscrollcommand=scrollbar.set)
+
+ text.bind_all("", lambda event: text.yview_scroll(-1 * int((event.delta / 120)), "units"))
+
+ button_frame = tk.Frame(self.edit_window)
+ button_frame.pack(fill="x", expand=False, pady=10)
+
+ def save_edit():
+ new_text = text.get("1.0", tk.END).strip()
+ self.itemconfig(self.cells[(row, col)][1], text=new_text[:cell_config["chars"]]) # Update with truncated text
+ global_df.iat[row, col] = new_text # Update the global DataFrame with full text
+ self.edit_window.destroy()
+ self.edit_window = None
+ self.update_scroll_region() # Update the scroll region after editing
+
+ save_button = tk.Button(button_frame, text="Save", command=save_edit)
+ cancel_button = tk.Button(button_frame, text="Cancel", command=lambda: [self.edit_window.destroy(), setattr(self, 'edit_window', None)])
+ save_button.pack(side="left", padx=20, pady=5)
+ cancel_button.pack(side="right", padx=20, pady=5)
+
+ text.focus_set()
+
+ def handle_return(event):
+ text.insert(tk.INSERT, "\n")
+ return "break"
+
+ text.bind('', handle_return)
+
+ # Update the scroll region after closing the edit window
+ self.edit_window.bind("", lambda event: [self.update_scroll_region(), setattr(self, 'edit_window', None)])
+
+ def update_scroll_region(self):
+ self.config(scrollregion=self.bbox("all"))
+ self.header_canvas.config(scrollregion=self.header_canvas.bbox("all"))
+ self.row_number_canvas.config(scrollregion=self.row_number_canvas.bbox("all"))
+
+ if self.xview() is not None:
+ self.header_canvas.xview_moveto(self.xview()[0])
+ if self.yview() is not None:
+ self.row_number_canvas.yview_moveto(self.yview()[0])
+
+ # Ensure headers and row numbers are always visible
+ self.tag_lower("header")
+ self.tag_lower("row_num")
+ self.parent.update()
+
+ # Re-bind scroll events
+ self.bind_all("", self.on_mouse_wheel)
+
+ def xview_handler(self, *args):
+ self.xview_moveto = super().xview_moveto
+ self.xview_scroll = super().xview_scroll
+ self.xview(*args)
+ self.header_canvas.xview(*args)
+ self.update_scroll_region()
+
+ def yview_handler(self, *args):
+ self.yview_moveto = super().yview_moveto
+ self.yview_scroll = super().yview_scroll
+ self.yview(*args)
+ self.row_number_canvas.yview(*args)
+ self.update_scroll_region()
+
+ def publish_selected_rows(self):
+ global current_selection
+ if current_selection is not None:
+ row1, col1, row2, col2 = current_selection
+ rows_to_publish = list(range(min(row1, row2) + 1, max(row1, row2) + 2))
+ result = publish_rows_to_disk(",".join(map(str, rows_to_publish)))
+ messagebox.showinfo("Publish Result", result)
+
+ def view_selected_rows(self):
+ global current_selection
+ if current_selection is not None:
+ row1, col1, row2, col2 = current_selection
+ rows_to_view = list(range(min(row1, row2) + 1, max(row1, row2) + 2))
+ result = view_html_pages(",".join(map(str, rows_to_view)))
+ messagebox.showinfo("View Result", result)
+
+# Ensure directory creation
+def ensure_dir(folder_path):
+ if not os.path.exists(folder_path):
+ os.makedirs(folder_path)
+
+# Write HTML to file
+def write_html(content, filename):
+ with open(filename, 'w', encoding='utf-8') as file:
+ file.write(content)
+
+def parse_line_numbers(line_numbers):
+ result = []
+ parts = line_numbers.split(',')
+ for part in parts:
+ if '-' in part:
+ start, end = map(int, part.split('-'))
+ result.extend(range(start - 1, end))
+ else:
+ result.append(int(part) - 1) # Convert to zero-based index
+ return result
+
+# Generate HTML Content
+def generate_html_content(row):
+ def check_nan(value):
+ return "" if pd.isna(value) else value
+
+ full_content = f"{check_nan(row['ourContent'])}{check_nan(row.get('Extra1', ''))}{check_nan(row.get('Extra2', ''))}"
+ directory_mode = str(row.get('directoryMode', 'false')).strip().lower() == 'true'
+ front_page = str(row.get('frontPage', 'false')).strip().lower() == 'true'
+ canonical_url = row['websiteUrl'] if front_page and not directory_mode else f"{row['websiteUrl']}{row['ourUrl']}/" if directory_mode else f"{row['websiteUrl']}{row['ourUrl']}.{row.get('fileExtension', 'html')}"
+ use_link_box = str(row.get('useLinkBox', 'false')).strip().lower() == 'true'
+
+ link_box_html = ""
+ if use_link_box:
+ link_box_html = f"""
The button below can help educators and others link to this page:
+
+
+
+ """
+
+ return f"""
+{check_nan(row['topHtml'])}
+
+ {check_nan(row['ourTitle'])}
+
+
+
+
+
+
+
+
+
+
+
+ {check_nan(row['headTag'])}
+
+
+
+ {check_nan(row['topMenu'])}
☰
+
+
{check_nan(row['ourTitle'])}
+ {full_content}
+ {link_box_html}
+ {check_nan(row['ourShareButton'])}
+
+
+
+
+
+"""
+
+
+
+# Construct the file path for a given row
+def construct_file_path(row):
+ folder_path = row['folderPath']
+ directory_mode = str(row.get('directoryMode', 'false')).strip().lower() == 'true'
+ file_name = os.path.join(row['ourUrl'], "index") if directory_mode else row['ourUrl']
+ file_extension = row.get('fileExtension', 'html')
+ return os.path.join(folder_path, f"{file_name}.{file_extension}")
+
+# Publish Rows to Disk
+def publish_rows_to_disk(line_numbers):
+ global global_df
+
+ if global_df is None:
+ messagebox.showwarning("No CSV", "No CSV loaded. Please load a CSV file first.")
+ return "No CSV loaded."
+
+ try:
+ lines_to_process = parse_line_numbers(line_numbers)
+ output = []
+
+ for index in lines_to_process:
+ if index < len(global_df):
+ row = global_df.iloc[index]
+ folder_path = row['folderPath']
+
+ directory_mode = str(row.get('directoryMode', 'false')).strip().lower() == 'true'
+ file_name = os.path.join(row['ourUrl'], "index") if directory_mode else row['ourUrl']
+ file_extension = row.get('fileExtension', 'html')
+ full_path = os.path.join(folder_path, f"{file_name}.{file_extension}")
+
+ ensure_dir(os.path.dirname(full_path))
+ html_content = generate_html_content(row)
+ write_html(html_content, full_path)
+ output.append(f"Processed row {index + 1} and saved to {full_path}")
+
+ return "\n".join(output)
+ except Exception as e:
+ return f"Error processing data: {str(e)}"
+
+# View Html Pages
+def view_html_pages(line_numbers=None):
+ global global_df
+
+ if global_df is None:
+ messagebox.showwarning("No CSV", "No CSV loaded. Please load a CSV file first.")
+ return "No CSV loaded."
+
+ try:
+ lines_to_open = parse_line_numbers(line_numbers)
+ files_to_open = []
+
+ for index in lines_to_open:
+ if index < len(global_df):
+ row = global_df.iloc[index]
+ file_path = construct_file_path(row)
+ files_to_open.append(file_path)
+
+ if not files_to_open:
+ messagebox.showwarning("No Files", "No valid files to open.")
+ return "No valid files to open."
+
+ for file in files_to_open:
+ webbrowser.open(f"file://{os.path.abspath(file)}", new=2)
+ return f"Opened {len(files_to_open)} files in the browser."
+ except Exception as e:
+ return f"Error opening files: {str(e)}"
+
+# Load CSV
+def load_csv():
+ global global_df
+ file_path = filedialog.askopenfilename(filetypes=[("CSV Files", "*.csv")])
+ if not file_path:
+ return
+ try:
+ global_df = pd.read_csv(file_path)
+
+ # Replace 0/1 with False/True for specific columns
+ bool_cols = ['directoryMode', 'useLinkBox', 'frontPage']
+ for col in bool_cols:
+ if col in global_df.columns:
+ global_df[col] = global_df[col].replace({0: "False", 1: "True"})
+
+ # Ensure columns are in the predefined order
+ global_df = global_df.reindex(columns=columns)
+
+ # Add extra empty rows if necessary to make sure there are at least 21 rows
+ if len(global_df) < 21:
+ extra_rows = 21 - len(global_df)
+ empty_rows = pd.DataFrame({col: [""] * extra_rows for col in columns})
+ global_df = pd.concat([global_df, empty_rows], ignore_index=True)
+
+ # Load the entire table to make all rows scrollable
+ update_table()
+ except Exception as e:
+ messagebox.showerror("Load Error", f"Failed to load CSV: {str(e)}")
+
+# Update Table
+def update_table(rows_to_display=None):
+ global global_df
+ if global_df is not None:
+ if rows_to_display is not None:
+ data = global_df.head(rows_to_display).fillna("").values.tolist()
+ else:
+ data = global_df.fillna("").values.tolist()
+ column_names = global_df.columns.tolist()
+ table.load_data(data, column_names)
+ table.update_scroll_region()
+
+
+# Save CSV
+def save_csv():
+ global global_df
+ if global_df is None:
+ messagebox.showwarning("No CSV", "No CSV loaded. Please load a CSV file first.")
+ return
+ file_path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV Files", "*.csv")])
+ if not file_path:
+ return
+ try:
+ global_df.to_csv(file_path, index=False)
+ messagebox.showinfo("CSV Saved", f"CSV file saved successfully to {file_path}.")
+ except Exception as e:
+ messagebox.showerror("Save Error", f"Failed to save CSV: {str(e)}")
+
+# Update Table
+def update_table(rows_to_display=None):
+ global global_df
+ if global_df is not None:
+ if rows_to_display is not None:
+ data = global_df.head(rows_to_display).fillna("").values.tolist()
+ else:
+ data = global_df.fillna("").values.tolist()
+ column_names = global_df.columns.tolist()
+ table.load_data(data, column_names)
+ table.update_scroll_region()
+
+# Process Button Action
+def process_button_action():
+ row_numbers = simpledialog.askstring("Rows to Process", "Enter rows to process (e.g., 0-2,4,6-8):")
+ if row_numbers:
+ result = publish_rows_to_disk(row_numbers)
+ messagebox.showinfo("Process Result", result)
+
+# Open Button Action
+def open_button_action():
+ row_numbers = simpledialog.askstring("Rows to Open", "Enter rows to open (e.g., 0-2,4,6-8):")
+ if row_numbers:
+ result = view_html_pages(row_numbers)
+ messagebox.showinfo("Open Result", result)
+
+# Main GUI Setup
+root = tk.Tk()
+root.title("Webster123pro")
+root.geometry("1200x600")
+
+toolbar = tk.Frame(root)
+toolbar.pack(side="top", fill="x", pady=10)
+
+load_button = tk.Button(toolbar, text="Load CSV", command=load_csv)
+load_button.pack(side="left", padx=5, pady=5)
+
+save_button = tk.Button(toolbar, text="Save CSV", command=save_csv)
+save_button.pack(side="left", padx=5, pady=5)
+
+frame = tk.Frame(root)
+frame.pack(fill="both", expand=True, pady=10)
+
+x_scrollbar = tk.Scrollbar(frame, orient="horizontal")
+x_scrollbar.grid(row=2, column=1, sticky='ew')
+
+y_scrollbar = tk.Scrollbar(frame, orient="vertical")
+y_scrollbar.grid(row=1, column=2, sticky='ns')
+
+# Use the SimpleTable class to create a custom table
+table = SimpleTable(frame)
+table.grid(row=1, column=1, sticky='nsew')
+
+# Configure scrollbars
+x_scrollbar.config(command=table.xview_handler)
+y_scrollbar.config(command=table.yview_handler)
+table.config(xscrollcommand=x_scrollbar.set, yscrollcommand=y_scrollbar.set)
+
+# Configure grid weights to make the table expandable
+frame.grid_rowconfigure(1, weight=1)
+frame.grid_columnconfigure(1, weight=1)
+
+# Initialize with initial data if no CSV is loaded
+if global_df is None:
+ global_df = pd.DataFrame(initial_data, columns=columns)
+ update_table()
+
+root.mainloop()
\ No newline at end of file