# create_post.py
import os
import datetime
import re
import argparse
def create_slug(title):
"""Generates a URL-friendly slug from a title."""
= title.lower()
slug = re.sub(r"[^\w\s-]", "", slug)
slug = re.sub(r"\s+", "-", slug)
slug = re.sub(r"-+", "-", slug)
slug = slug.strip("-")
slug return slug
def get_user_input(prompt_text, default_value=None):
"""Gets user input with an optional default value."""
if default_value:
= f"{prompt_text} (default: {default_value}): "
prompt_with_default else:
= f"{prompt_text}: "
prompt_with_default
= input(prompt_with_default).strip()
user_value return user_value if user_value else default_value
def main():
= argparse.ArgumentParser(description="Create a new Quarto blog post.")
parser
parser.add_argument("--dir",
type=str,
="posts",
defaulthelp="Directory to save the post (e.g., 'posts', '_posts'). Can be set non-interactively.",
)= parser.parse_args()
args dir = args.dir
print("Creating a new Quarto blog post...")
print("-" * 35)
# Title (Required)
while True:
= input("Post title: ").strip()
title if title:
break
print("Title cannot be empty. Please enter a title.")
= "DEFAULT AUTHOR NAME"
default_author = get_user_input("Enter author", default_value=default_author)
author
# Description
= get_user_input(
description "Enter a short description (optional, press Enter to skip)"
)
# Date
= datetime.date.today().strftime("%Y-%m-%d")
default_date while True:
= get_user_input("Enter date (YYYY-MM-DD)", default_value=default_date)
date_str try:
"%Y-%m-%d")
datetime.datetime.strptime(date_str, break
except ValueError:
print("Invalid date format. Please use YYYY-MM-DD.")
# Folder name
= create_slug(title)
suggested_slug = f"{date_str}_{suggested_slug}"
suggested_foldername = get_user_input("Enter folder name", default_value=suggested_foldername)
foldername
# Categories
= get_user_input(
categories_input "Enter categories (comma-separated, e.g., news,python,quarto) (optional)"
)= ""
categories_yaml_lines if categories_input:
= [cat.strip() for cat in categories_input.split(",") if cat.strip()]
cats = (
categories_yaml_lines "categories:\n" + "\n".join(f" - {cat}" for cat in cats) if cats else ""
)
# Draft status
= get_user_input("Mark as draft? (yes/no)", default_value="no").lower()
draft_input = draft_input in ["yes", "y"]
draft
# Create directory
= os.path.join(dir, foldername)
post_dir if os.path.exists(post_dir):
= get_user_input(
overwrite f"Warning: Post '{post_dir}' already exists. Overwrite? (yes/no)",
="no",
default_value
).lower()if overwrite not in ["yes", "y"]:
print("Post creation cancelled.")
return
=True)
os.makedirs(post_dir, exist_ok= os.path.join(post_dir, "index.qmd")
output_path
# --- Create YAML Header and Content ---
= f"""---
content title: "{title}"
description: "{description if description else ""}"
date: "{date_str}"
author: "{author}"
date-modified: "{date_str}"
draft: {str(draft).lower()}
{categories_yaml_lines}
---
Your content goes here.
"""
try:
with open(output_path, "w", encoding="utf-8") as f:
f.write(content)print("-" * 30)
print(f"Blog post successfully created: {output_path}")
print("-" * 30)
except IOError as e:
print(f"Error writing file: {e}")
if __name__ == "__main__":
main()
Although I am not a big fan of generating entire code using generative AI, I find it helpful to automate repetitive tasks that would otherwise take longer if done manually. In this case, I am creating a Quarto post template using Python. The initial code provided by the LLM was good enough, but I needed to make minor adjustments to fit my needs. While you can also prompt it yourself, you can use the following Python script to generate a Quarto template:
You can run the Python script from the command line using:
uv run create_post.py
Back to topReuse
Citation
For attribution, please cite this work as:
Reynaldo Hutabarat, Farhan. 2025. “On Automating Post
Creation.” May 14, 2025. https://weaklyinformative.com/posts/2025-05-14_on-automating-post-creation/.