• AI CODING CLUB
  • Posts
  • 10 minutes to code a script which saved my client 6 hours

10 minutes to code a script which saved my client 6 hours

Simple Python automations are the most satisfying type of code to ship!

Before sharing one of my latest Python scripts, I'd like to take a moment to highlight an interview that I highly recommend: Pieter Levels' conversation with Lex Fridman.

If you haven't heard it yet, I strongly encourage you to check it out on your preferred podcast platform (Youtube Link).

Pieter shared the full story of his entrepreneurial journey, which led him to launch a series of SaaS products that now collectively generate over $230K per month—an impressive achievement for a solopreneur and indie hacker.

Pieter Levels’ portfolio

When asked how to learn coding, Pieter offered the simplest yet most powerful advice: TAKING ACTION IS THE BEST WAY TO LEARN.

You can binge-watch YouTube videos, purchase courses, and read books, but nothing compares to the lessons you'll gain from actually building and shipping a product.

That's exactly how I did it—starting with Python scripts to automate SEO research and content creation, and then moving on to micro SaaS applications, both for myself and a select group of clients.

One of these projects, the AI Jingle Maker, which I launched on September 8, 2023, has just surpassed 30,000 users and nearly reached $5K MRR in August.

It is fully AI-coded.

No staging, straight to prod!

Pieter also revealed that he doesn't use a staging environment—he ships directly to production, committing over 30K times per year.

This approach has sparked criticism on X and LinkedIn, with some calling it highly unprofessional. However, it's the method that Pieter and many other indie hackers use to successfully ship and maintain a large number of apps.

When done iteratively and with thorough local testing, deploying straight to production doesn't have to be risky. You can always roll back to a previous deployment if something goes wrong. This is the same approach I take: I ship all my iterations straight from VS Code to GitHub, and from GitHub to Railway.

To minimize risks, I isolate new backend functions into separate .py files that are called by my main Flask .py file. The same goes for dedicated JavaScript logic, which I keep in separate .js files within my static folder.

Like Pieter, I don’t rely on any fancy frameworks. I stick to HTML, vanilla JavaScript, and CSS (including Tailwind). It might seem old school, but it works for me—just as coding in PHP and jQuery works for Pieter.

The interview is packed with no-nonsense tips for indie hackers.

Now, back to the script I wanted to share in this newsletter.

Simple & Effective

I love simple Python automations—they’re the most satisfying type of code you can ship.

Below, you’ll find a script that allowed me to remotely create dozens of categories and tags in bulk across multiple WordPress sites, saving my client at least six hours of work.

It took just 10 minutes to write the script with ChatGPT 4.0, and generating tens of categories and tags (including tag descriptions) for six websites took less than five minutes.

Here’s the prompt I gave to ChatGPT, using the code I already had for creating single categories and tags, along with category and tag translations (since my client manages multilingual websites). In this case, my client generates CSV exports from an Airtable table, but the process could easily work with data from a simple Google Sheet. We could even connect the script directly to Airtable or Google Sheets.

PROMPT TO CHATGPT 4o

I need to use the data from a CSV file to create categories and tags on my WordPress site ("tag" is presented as "theme" in this CSV). 

The categories and tags must be created using those functions I already have in my code:

def create_wordpress_taxonomy(domain, taxonomy, name, wordpress_head_post, language_code):

    api_url = f'https://{domain}/wp-json/wp/v2/{taxonomy}'

    # data structure, in JSON
    data = {
        "name": name,
        "status": "publish",
        "lang": language_code  
    }

    response = requests.post(api_url, headers=wordpress_head_post, json=data)

    results = response.json()

    return results['id']

def create_wordpress_taxonomy_translation(domain, taxonomy, name, wordpress_head_post, language_code, original_taxonomy_id):

    api_url = f'https://{domain}/wp-json/wp/v2/{taxonomy}'

    # data structure, in JSON
    data = {
        "name": name,
        "status": "publish",
        "lang": language_code,  
        "translations": {language_code: original_taxonomy_id}  
    }

    response = requests.post(api_url, headers=wordpress_head_post, json=data)

    results = response.json()

    return results['id']


Taking into account the suffixes in the column names in the CSV.

The main language being _fr

Please give me the Python script to handle the creation of the taxonomies for the site example.com 

ChatGPT gave me a fully functional script but I needed to refine it after my client asked me to add a description for each tag.

How can we adapt the script to also register for tags the tag description, which we'll find in the CSV for the main language in the METAPAGE INTO_fr column and for the other ones in METAPAGE INTRO_en, _es, _nl, _de, _it
 

Here’s the full code, which you can run from a .py file.

You’ll need of course to replace the WordPress credentials with your own data.

The WP application password is stored in a .env file.

import pandas as pd
import requests
import os
import base64
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Load CSV data
csv_file_path = 'data.csv'
df = pd.read_csv(csv_file_path)

# Define your WordPress domain and headers (you need to replace with actual domain and headers)
wp_user = "[email protected]"
wp_pw = os.environ['WEBSITE_WP_PW']
domain = "example.com"
cpt = "publication"

def wordpress_header(wp_user, wp_pw):
    wordpress_user = wp_user
    wordpress_password = wp_pw
    wordpress_credentials = wordpress_user + ":" + wordpress_password
    wordpress_token = base64.b64encode(wordpress_credentials.encode())
    wordpress_header = {'Authorization': 'Basic ' + wordpress_token.decode('utf-8')}
    return wordpress_header

wordpress_head_post = wordpress_header(wp_user, wp_pw)

# Define functions
def create_wordpress_taxonomy(domain, taxonomy, name, description, wordpress_head_post, language_code):
    api_url = f'https://{domain}/wp-json/wp/v2/{taxonomy}'
    data = {
        "name": name,
        "description": description,
        "status": "publish",
        "lang": language_code
    }
    response = requests.post(api_url, headers=wordpress_head_post, json=data)
    
    # Debugging: Print the response from the API
    print(f"Creating {taxonomy} '{name}' in '{language_code}' language: {response.status_code} - {response.text}")
    
    if response.status_code == 201:
        results = response.json()
        return results['id']
    else:
        print(f"Failed to create {taxonomy} '{name}' in '{language_code}': {response.status_code} - {response.text}")
        return None

def create_wordpress_taxonomy_translation(domain, taxonomy, name, description, wordpress_head_post, language_code, original_taxonomy_id):
    api_url = f'https://{domain}/wp-json/wp/v2/{taxonomy}'
    data = {
        "name": name,
        "description": description,
        "status": "publish",
        "lang": language_code,
        "translations": {language_code: original_taxonomy_id}
    }
    response = requests.post(api_url, headers=wordpress_head_post, json=data)
    
    # Debugging: Print the response from the API
    print(f"Creating translation for {taxonomy} '{name}' in '{language_code}' language: {response.status_code} - {response.text}")
    
    if response.status_code == 201:
        results = response.json()
        return results['id']
    else:
        print(f"Failed to create translation for {taxonomy} '{name}' in '{language_code}': {response.status_code} - {response.text}")
        return None

# Process the DataFrame and create categories and tags
for index, row in df.iterrows():
    # Handle Categories
    category_fr = row.get('CATEGORY_fr', '').strip()
    if category_fr:
        category_id_fr = create_wordpress_taxonomy(domain, 'categories', category_fr, '', wordpress_head_post, 'fr')
        
        if category_id_fr:
            # Handle translations
            for lang_suffix in ['en','es','de', 'nl','it']:
                category_trans = row.get(f'CATEGORY_{lang_suffix}', '').strip()
                if category_trans:
                    create_wordpress_taxonomy_translation(domain, 'categories', category_trans, '', wordpress_head_post, lang_suffix, category_id_fr)
    
    # Handle Themes (Tags)
    theme_fr = row.get('THEME_fr', '').strip()
    theme_desc_fr = row.get('METAPAGE INTRO_fr', '').strip()
    if theme_fr:
        theme_id_fr = create_wordpress_taxonomy(domain, 'tags', theme_fr, theme_desc_fr, wordpress_head_post, 'fr')
        
        if theme_id_fr:
            # Handle translations
            for lang_suffix in ['en','es','de', 'nl','it']:
                theme_trans = row.get(f'THEME_{lang_suffix}', '').strip()
                theme_desc_trans = row.get(f'METAPAGE INTRO_{lang_suffix}', '').strip()
                if theme_trans:
                    create_wordpress_taxonomy_translation(domain, 'tags', theme_trans, theme_desc_trans, wordpress_head_post, lang_suffix, theme_id_fr)

print("Categories and tags creation process completed.")

Note: If you copy and paste the code into your IDE, you may need to fix the indents.

If you manage multiple multilingual WordPress projects and need to set up categories and tags, this script will save you hours of manual work. Simply create a CSV for each site. My client used "theme" for "tag" (so his columns are named theme_fr, theme_eng, etc.), but you can just as easily use "tag" and name your columns tag_fr, tag_eng, etc.

If you have any questions about this code or want to develop something slightly different, feel free to reply to this email. I’ll do my best to assist you.

One-on-one Coaching Available

If you’d like to get a private introduction to the art of AI-assisted coding and more broadly a detailed overview of today’s Gen AI capabilities, I’m offering one-on-one 2-hour mentoring sessions “How To Talk To An AI Agent”.

Here’s the link for bookings.

Sessions are tailored to your specific business needs.

I can also assist you in the development of your own micro SaaS project.