Hugo Create Site Structure Template
If you need to create a site skeleton quickly from a 2 column xlsx file, this script will do that for you. Modify the front matter template to your desired output. It should be run it’s own venv and then the resulting files copied to your hugo project.
It is designed to work specifically with the TechDoc Theme I’m using on this site and creates the automatic menus and front matter template for later editing with most parts filled out.
If you had 5 rows in an xlsx file, the first row being a header and the rows being something like:
MAIN,SUB, N/A,N/A
fruit, apple, N/A,N/A
fruit, peach, N/A,N/A
veg, spinash, N/A,N/A
veg, broc broc oii, N/A,N/A
It sould create a folder structure like the image with fruit and veg being main categories and the other items being sub categories, columns 3 and 4 are ignored in this script, but it can be modified to create affliate sites, shopping carts or XLSX driven starter doc templates too.
Requirements.txt
et-xmlfile==1.1.0
openpyxl==3.1.2
pytz==2023.3.post1
Create Main/Sub from XLSX
If you have an XLSX like the xample, this will create main and sub catetory skeleton structure for you.
import tkinter as tk
from tkinter import scrolledtext, filedialog
import os
import openpyxl
import pytz
from datetime import datetime, timezone
template_index_header = '''---
draft = false
author = "James Fraze"
description = ""
tags = ["", ""]
images = ["/i/x.png"]
'''
template_index_footer = '''---
{{ jameslist }}
'''
def load_file():
global excel_data
excel_data = []
file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")])
if file_path:
try:
workbook = openpyxl.load_workbook(file_path)
sheet = workbook.active
for row in sheet.iter_rows(min_row=2): # Assuming first row is header
main_category = row[0].value
sub_category = row[1].value if len(row) > 1 else None
if main_category:
excel_data.append((main_category, sub_category))
else:
output_text.insert("1.0", "Error: Some rows have missing data in the first column.\n")
return
output_text.insert("1.0", "File loaded successfully. Ready to Hugofy!\n")
except Exception as e:
output_text.insert("1.0", f"Error reading file: {e}\n")
def normalize_string(s):
return s.lower().replace(" ", "-")
def hugofy():
output_text.delete("1.0", "end")
hugo_dir = "hugo"
os.makedirs(hugo_dir, exist_ok=True) # Ensure the hugo directory exists
created_main_folders = set()
main_weight = 100
for main_category, sub_category in excel_data:
sub_weight = 100
main_folder = normalize_string(main_category)
main_folder_path = os.path.join(hugo_dir, main_folder)
if main_folder not in created_main_folders:
os.makedirs(main_folder_path, exist_ok=True) # Create main folder path
main_weight += 100
created_main_folders.add(main_folder)
output_text.insert(tk.END, f"Created main folder: {main_folder_path}\n")
create_index_md(main_folder_path, main_category, main_category, main_folder, main_weight)
if sub_category:
sub_folder = normalize_string(sub_category)
full_path = os.path.join(main_folder_path, sub_folder)
os.makedirs(full_path, exist_ok=True) # Create sub folder path
sub_weight += 100
output_text.insert(tk.END, f"Created sub folder: {full_path}\n")
create_index_md(full_path, main_category, sub_category, sub_folder, sub_weight)
def create_index_md(folder_path, main_category, title, slug, weight):
tz = pytz.timezone('America/Chicago')
current_datetime = datetime.now(tz).isoformat(timespec='seconds')
categories = [main_category.lower(), slug.lower()] if main_category != title else [main_category.lower()]
print(slug,categories)
# Adjust folder_path for the URL in front matter
relative_path = folder_path.replace("hugo/", "")
dynamic_header = f'''
url = "/{relative_path}"
categories = {categories}
slug = "{slug}"
title = "{title}"
date = "{current_datetime}"
lastmod = "{current_datetime}"
weight = {weight}
'''
index_md_path = os.path.join(folder_path, "_index.md")
if not os.path.exists(index_md_path):
with open(index_md_path, 'w') as f:
template_full = template_index_header + dynamic_header + template_index_footer
f.write(template_full)
output_text.insert(tk.END, f"Created file: {index_md_path}\n")
# Create the main window
root = tk.Tk()
root.title("Hugo Template Generator")
root.geometry("1200x400")
# Create a Grid layout
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(1, weight=8) # 80% for output
# Create a Text widget for output
output_text = scrolledtext.ScrolledText(root, height=15, width=120)
output_text.grid(row=0, column=1, sticky='nsew', padx=5, pady=5)
# Create a Frame for buttons
button_frame = tk.Frame(root)
button_frame.grid(row=1, column=0, columnspan=2, pady=5)
# Create buttons
hugofy_button = tk.Button(button_frame, text="Hugofy", command=hugofy)
hugofy_button.pack(side=tk.LEFT, padx=5)
load_button = tk.Button(button_frame, text="Load", command=load_file)
load_button.pack(side=tk.LEFT, padx=5)
# Start the GUI event loop
root.mainloop()
Create Main from Text Input
If you have a site structure with only 1 level deep, this will create that structure in the order you specify.
import tkinter as tk
from tkinter import scrolledtext, filedialog
import os
import openpyxl
import pytz
from datetime import datetime, timezone
sample1 = '''Blues
Christmas
Civil War
Classical
Country
Disney
Folk
Grunge
Irish
Kids
Metal
Movies
Rock
Scales
Spanish
Video Game
Worship'''
sample2 = '''Overview
Training
Drills
SOP + Check Lists
Client Forms
Newsletters
Marketing
Meal Preps
Account Logins
Onboarding Staff
Billing
Funding
Construction
Contractors
Permits + Regulations
Insurance
Taxes
'''
template_index_header = '''---
draft = false
author = "James Fraze"
description = ""
tags = ["", ""]
weight = 1
images = ["/i/x.png"]
'''
template_index_footer = '''---
{{ jameslist }}
'''
def hugofy():
output_text.delete("1.0", "end")
hugo_dir = "hugo"
os.makedirs(hugo_dir, exist_ok=True) # Ensure the hugo directory exists
created_main_folders = set()
# Read data from input_text and process each line
input_data = input_text.get("1.0", tk.END).strip().split('\n')
weight = 100
for line in input_data:
# Assuming each line contains only the main category
main_category = line.strip()
if not main_category:
continue # Skip empty lines
main_folder = normalize_string(main_category)
main_folder_path = os.path.join(hugo_dir, main_folder)
if main_folder not in created_main_folders:
weight += 100
created_main_folders.add(main_folder)
output_text.insert(tk.END, f"Created main folder: {main_folder_path}\n")
create_index_md(main_folder_path, main_category, main_category, main_folder, weight)
def normalize_string(s):
return s.lower().replace(" ", "-")
def create_index_md(folder_path, main_category, title, slug, weight):
tz = pytz.timezone('America/Chicago') # CST Timezone
current_datetime = datetime.now(tz).isoformat(timespec='seconds')
categories = [main_category.lower(), slug.lower()] if main_category != title else [main_category.lower()]
dynamic_header = f'''
url = "/{slug}"
categories = {categories}
slug = "{slug}"
title = "{title}"
date = "{current_datetime}"
lastmod = "{current_datetime}"
weight = {weight}
'''
# Ensure the folder exists before creating the file
os.makedirs(folder_path, exist_ok=True)
index_md_path = os.path.join(folder_path, "_index.md")
if not os.path.exists(index_md_path):
with open(index_md_path, 'w') as f:
template_full = template_index_header + dynamic_header + template_index_footer
f.write(template_full)
output_text.insert(tk.END, f"Created file: {index_md_path}\n")
def clear_texts():
input_text.delete("1.0", "end")
output_text.delete("1.0", "end")
def load_sample(sample):
input_text.delete("1.0", "end")
input_text.insert("1.0", sample)
# Create the main window
root = tk.Tk()
root.title("Hugo Template Generator")
root.geometry("1200x400")
# Create a Grid layout
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=2) # 20% for input
root.grid_columnconfigure(1, weight=8) # 80% for output
# Create a Text widget for input
input_text = scrolledtext.ScrolledText(root, height=15, width=30)
input_text.grid(row=0, column=0, sticky='nsew', padx=5, pady=5)
# Create a Text widget for output
output_text = scrolledtext.ScrolledText(root, height=15, width=120)
output_text.grid(row=0, column=1, sticky='nsew', padx=5, pady=5)
# Create a Frame for buttons
button_frame = tk.Frame(root)
button_frame.grid(row=1, column=0, columnspan=2, pady=5)
# Create buttons
hugofy_button = tk.Button(button_frame, text="Hugofy", command=hugofy)
hugofy_button.pack(side=tk.LEFT, padx=5)
# Create buttons for each sample
sample_button1 = tk.Button(button_frame, text="Load Sample 1", command=lambda: load_sample(sample1))
sample_button1.pack(side=tk.LEFT, padx=5)
sample_button2 = tk.Button(button_frame, text="Load Sample 2", command=lambda: load_sample(sample2))
sample_button2.pack(side=tk.LEFT, padx=5)
clear_button = tk.Button(button_frame, text="Clear", command=clear_texts)
clear_button.pack(side=tk.LEFT, padx=5)
# Start the GUI event loop
root.mainloop()