Learning Python in 2020 – Beginner – Simple Automated WordPress Project Setup

Requirements

Python, internet, time

Problem

Goal

The script should simply download the latest wordpress zip file, unzip it and allow the user to set the main wp-config values.

Pseudo-code

  1. Make a get request ( https://wordpress.org/latest.zip )
  2. Save the downloaded zip file in the current directory
  3. Create a wp-config.php file based off wp-config-sample.php file
  4. Ask the user for the wp-config values

Solution

Miscellaneous functions – Path handling

def checkpathexists(path: str) -> bool:
    return os.path.exists(path)


def deletedir(path: str):
    print("deleting directory: {}".format(path))
    return shutil.rmtree(path)


def deletefile(path: str):
    print("deleting file: {}".format(path))
    return os.remove(path)

These are functions that only play minor roles in the code logic.

Check if the path exists?

The first function uses the os.path module’s existing method, which checks if the given path exists. We will use this method to confirm if the wordpress zip file or wordpress zip folder exists. The method’s return value is of type boolean.

os.path.exists(path: str) -> bool

Delete directory and file

In case of an already existing wordpress.zip or wordpress folder, either of the functions deletedir or deletefile are to be applied. The os module also provides a remove method which, according to its documentation, can remove a file based on the provided file path. Note that if the path given points to a folder/directory, an error will be thrown. Also make sure the file isn’t open while running the script due to permission issues.

os.remove(path, *, dir_fd: int) -> None

While one could use the os.removedirs method to remove directories recursively, I decided to use the shutil module’s rmtree method. After reading here, you’ll see the former would throw an error if the directory isn’t empty. The latter actually does what we want.

Formatting strings using variables

To learn more about cool ways to generate and format strings in python, do some exercises here.

Making Requests – Download WordPress Zip File

def download_wordpress():
    print("Wordpress download initiating...")
    if checkpathexists("wordpress.zip"):
        delete = input("should I delete your wordpress zip file?: y/n     ")
        if delete == "y":
            deletefile("wordpress.zip")
        else:
            print("the script failed, because you won't let me delete stuff. hmpf.")
            exit()
    url = "https://wordpress.org/latest.zip"
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with open("wordpress.zip", "wb") as f:
            shutil.copyfileobj(r.raw, f)
    print("Wordpress download completed...")
    return

Getting input from user

The python’s input function takes in a string as an argument and returns the user’s input provided over the command line.

input(prompt)

Fetching data over http

In JS, making http requests can be done by axios or the fetch function. In python, the requests module offers methods with which http requests are sent..Since I am simply fetching data and not sending one, the GET verb should suffice. Learn more about http verbs here.

The response object returned from the get call contains the downloaded wordpress zip file needed. Just in case the wordpress file is large, we will enable sending the large file in chunks rather than all at once by setting stream to true. Read here for more

With.. as statement

This is based on my understanding a cleaner and safer way to properly close an actions involving file handling and data manipulation. Read

Manipulating files – Unzipping zip files

def unzip_wordpress():
    print("Wordpress unzipping initiating...")
    if checkpathexists("wordpress"):
        delete = input("should I delete your wordpress folder?: y/n     ")
        if delete == "y":
            deletedir("wordpress")
        else:
            print("the script failed, because you won't let me delete stuff. hmpf.")
            exit()
    with zipfile.ZipFile("wordpress.zip", "r") as zip_ref:
        zip_ref.extractall(".")
    print("Unzipping wordpress file completed....")
    return

Zip up!

Module here.

File handling – wp-config.php from wp-config-sample.php

def duplicate_file(source, destination):
    shutil.copyfile(source, destination)
    print("Copying file {} to {} was successful...".format(source, destination))
    return

After unzipping the wordpress file and you move into the folder, you should notice that the main wp-config.php is absent. Since the wp-config-sample.php file is identical to wp-config.php, we only need to duplicate and rename the file.

Functions and I/O : Configure wp-config.php via command line

def replace_line_in_source(findstr, replacement, filepath):
    with open(filepath, "rt") as f:
        data = f.read()
        data = data.replace(findstr, replacement)
        f.close()
        with open(filepath, "wt") as fw:
            fw.write(data)
            fw.close()
    print("The line {} was replaced with {} in file {}".format(
        findstr, replacement, filepath))
    return

The replace line source function looks for a given string findstr in a given file (at filepath) and when found, replaces the found string with the given replacement.

Default Configuration

def fill_wp_config():
    config = {
        "define( 'DB_NAME', 'database_name_here' )": {"field": "DB_NAME", "replacement": "database_name_here"},
        "define( 'DB_USER', 'username_here' )":  {"field": "DB_USER", "replacement": "username_here"},
        "define( 'DB_PASSWORD', 'password_here' )":  {"field": "DB_PASSWORD", "replacement": "password_here"},
        "define( 'DB_HOST', 'localhost' )":  {"field": "DB_HOST", "replacement": "localhost"},
        "define( 'DB_CHARSET', 'utf8' )":  {"field": "DB_CHARSET", "replacement": "utf8"},
        "define( 'DB_COLLATE', '' )":  {"field": "DB_COLLATE", "replacement": ""}
    }

    for key in config:
        definition = config[key]
        field = definition["field"]
        replacement = definition["replacement"]
        user_replacement = input(
            "Please type in the replacement for [" + field + "]  or press ENTER to type nothing:     ")
        if user_replacement and len(user_replacement) > 0:
            definition["replacement"] = user_replacement
        r = definition["replacement"]
        valid_replacement = "define( '{}', '{}' )".format(field, r)
        replace_line_in_source(key, valid_replacement,
                               "wordpress/wp-config.php")
    print("All fields replaced")
    return

I have a feeling there exists a better way to implement this logic than this overly-complicated blob. However, with my mediocre programming background this was what I could come up with. Apologies.

Simply, I stored the 6 important configurations in wp-config.php in a dictionary; loop over the keys and ask the user for their corresponding values. If the user provides a value, it is replaced. Else, the default value is retained.

Run Program

def start():
    download_wordpress()
    unzip_wordpress()
    duplicate_file("wordpress/wp-config-sample.php", "wordpress/wp-config.php")
    fill_wp_config()

Complete Code

Question

  1. How would you create a function that generates a mysql database automatically and specifically for the WordPress project ?

Author Notes

I learned a lot of basic python syntax by means of this exercise. If you have suggestions, please type them below. Thanks a lot.

Links

  1. https://requests.readthedocs.io/en/master/
  2. https://docs.python.org/3/tutorial/errors.html
  3. https://stackoverflow.com/questions/16694907/download-large-file-in-python-with-requests
  4. https://stackoverflow.com/questions/3451111/unzipping-files-in-python
  5. https://www.geeksforgeeks.org/python-shutil-copyfile-method/
  6. https://pythonexamples.org/python-replace-string-in-file/
  7. https://www.w3schools.com/python/python_lists.asp
  8. https://realpython.com/iterate-through-dictionary-python/
  9. https://www.geeksforgeeks.org/taking-input-from-console-in-python/
  10. https://www.w3schools.com/python/ref_string_split.asp
  11. https://linuxize.com/post/python-delete-files-and-directories/
  12. https://stackoverflow.com/questions/19782075/how-to-stop-terminate-a-python-script-from-running/34029481
  13. https://docs.python.org/2/library/os.path.html
  14. https://docs.python.org/2/library/webbrowser.html
  15. https://thispointer.com/python-how-to-delete-a-directory-recursively-using-shutil-rmtree/