The Carnyx

Jan 5, 2021

I stumbled upon the following museum lecture from 2015 on YouTube, highlighting the carnyx, a Celtic war horn that was used thousands of used ago (and only recently reconstructed!). If you have 45 minutes and an interest in either history or music, I highly recommend watching.

One of my personal goals for 2021 is to increase my writing frequency here at this website. A little over 14 years ago (!) I did some analysis on my posting habits, and I thought I'd do something similar to see how my posting has changed over time. Using a quick Python script and matplotlib, the latter of which I don't use frequently enough to ever remember how to use, I came up with the following two diagrams:

Both graphs show how, in recent years, my posting frequency has really dropped off a cliff (having a kid will do that to you, I guess). Even prior to kids, however, I was posting much less frequently than I did in the beginning.

Now that I have a new publishing platform, posting has gotten easier for me. My personal goal is to post at least three times a month, which should put me on par with 2011, which is the last time I averaged that total.

Python showPath Script

Dec 27, 2020

I occasionally have a need to either view the PATH environment variable from the command line, or search the PATH for something. I wrote a small Python script to make this easy to do, adapting it from an old Perl script I wrote years ago. The script, in its entirety, is shown below (you can also download it here; just save it as a Python script). Note that this script currently has a Windows focus, but could easily be adjusted to work in Linux too.

When used by itself, the script will simply pretty-print all of the paths currently in your environment's PATH. You can pass a --sort option to sort the output, or you can supply a needle to search for. Hopefully someone else will find this as useful as I do.

#!/usr/bin/python
import argparse
import os


parser = argparse.ArgumentParser()
parser.add_argument('searchterm', default='', nargs='?')
parser.add_argument('--sort', action='store_true', default=False,
                    help='Print PATH in a sorted form')

args = parser.parse_args()

path = os.getenv('PATH').split(';')

if args.sort:
    path = sorted(path)

if args.searchterm:
    needle = args.searchterm.lower()
    print(f"\nSearching PATH for {needle}")

    matches = []
    for p in path:
        if needle in p.lower():
            matches.append(p)

    if matches:
        print(f"Found {len(matches)} result{'' if len(matches) == 1 else 's'}")
        for m in matches:
            print(m)
    else:
        print(f"Unable to find {needle} in PATH")
else:
    print("\nShowing PATH:\n")
    for p in path:
        print(p)

Django 3.1 now uses the Python pathlib module for its internal paths. This change caught me off guard when I started developing with it, as I was used to the old os.path way of doing things. Here's a look at the old way and its newer counterpart:

# Old way
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# New way
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent

Joining paths together uses a very different mechanism as well:

# Old way
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

# New way
STATIC_ROOT = BASE_DIR / 'static'

# Alternate new way (which I prefer)
STATIC_ROOT = BASE_DIR.joinpath('static')

These new mechanisms feel so different since they treat paths as objects, not strings. This works well, however, since paths aren't really strings in the first place. Transitioning to this new way of thinking is taking me some time, since prior to this module, every path in Python was treated as a string. However, I can already see the utility of this module, especially when it comes to resolving relative paths.

Hummus

Dec 21, 2020

We got rid of our Better Homes and Gardens cookbook, so I'm transcribing one of its recipes here so we can remember it. This makes pretty good hummus, great with pita bread or pita chips.

Ingredients:

  • 1 15-ounce can garbanzo beans (chickpeas), rinsed and drained
  • 1 clove garlic, minced
  • 1/4 cup tahini
  • 1/4 cup lemon juice
  • 1/4 cup olive oil
  • 1/2 tsp salt
  • 1/4 tsp paprika

Steps:

  1. In a blender or food processor, combine garbanzo beans, garlic, tahini, lemon juice, olive oil, salt, and paprika. Cover and blend until smooth, scraping the sides as necessary.
  2. If desired, add one or more stir-ins of your choice (e.g. sliced green onions, roasted red peppers, etc.).

Makes about 1-3/4 cups.

The dependency resolver in Python's pip command was recently updated in version 20.3. This fundamental change has a number of improvements, but I discovered today a serious drawback of this new machinery. Using the previous resolver, pip allowed you do the following to discover what versions of a package were available:

pip install markdown==

This command provided output like the following:

ERROR: Could not find a version that satisfies the requirement markdown==
(from versions: 1.7, 2.0, 2.0.1, 2.0.2, 2.0.3, 2.1.0, 2.1.1, 2.2.0, 2.2.1,
2.3, 2.3.1, 2.4, 2.4.1, 2.5, 2.5.1, 2.5.2, 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4,
2.6.5, 2.6.6, 2.6.7, 2.6.8, 2.6.9, 2.6.10, 2.6.11, 3.0, 3.0.1, 3.1, 3.1.1,
3.2, 3.2.1, 3.2.2, 3.3, 3.3.1, 3.3.2, 3.3.3)
ERROR: No matching distribution found for markdown==

This trick was often useful to discover what new versions (if any) of required packages are available. Sadly, the new machinery no longer produces output like the above. Instead, all you get is this entirely unhelpful message:

ERROR: Could not find a version that satisfies the requirement markdown==
ERROR: No matching distribution found for markdown==

An open bug in the pip project is tracking this issue, but most of the developer responses so far have been of the "we don't have the funding to fix this" variety. There are a number of recommended solutions in the ticket, none of which seem as simple as the previous trick. Hopefully this is something that can be prioritized and fixed soon.

During the deploy process of my new web platform, I ran into a really puzzling situation. I had my application deployed per DreamHost's documentation, but I kept getting the standard domain landing page ("borngeek.com is almost here!"). This deploy was set up exactly like several other sites I had working successfully, but this one just wasn't working.

I ended up having to reach out to DreamHost support (which is fantastic, by the way), and one of their technicians helped me figure out the issue. Prior to deploying this new Django-powered platform, I was running with WordPress. While running in that environment, I had enabled HTTPS support through the Let's Encrypt service, something DreamHost offers for free. The HTTPS service was configured with a path that no longer applied, seeing as my new folder structure is different from the old WordPress setup.

There are two ways the pathing issue can be fixed at DreamHost:

  1. You can reach out to the support team to have them ensure that the HTTPS service is pointing at the right location.
  2. You can remove the HTTPS certificate from the domain, and request a new one. The path will be updated as a byproduct of this change.

Hopefully this tip will help someone else out there, as I spent several frustrating hours debugging this issue.

Mark Shields

Dec 19, 2020

I don't watch the news, and I generally despise politics. However, I routinely make time each week to watch Shields and Brooks on the PBS NewsHour. Mark Shields and David Brooks both seem like people I would enjoy knowing in real life. They are intelligent, thoughtful, and they clearly respect each other. It is so refreshing to hear two people disagree about things in a calm, respective manner.

It's sad news, then, that Mark is stepping down after 33 years. I'll really miss his insights and his funny one-liners. Here's a short video that the NewsHour put together to honor his work over the years.

Spam Mitigation

Dec 18, 2020

One of my primary concerns in writing my own web publishing platform was how to manage spam. In the WordPress world I used both Akismet, as well as another third-party plugin, to keep things under control. In looking around at various options, I stumbled upon a terrific article at Ned Batchelder's blog on how he manages spam.

His technique can prevent both playback bots, as well as form-filling bots, from submitting garbage data. The process is fairly basic:

  1. A timestamp field is inserted as a part of the commenting form.
  2. A spinner field is included, its value being a hash of four key data elements:
    • The timestamp
    • The client's IP address
    • The entry ID of the post being commented on
    • A secret
  3. Field names on the form are all randomized, with the exception of the spinner. The randomization process uses the spinner value, the real field name, and a secret.
  4. Honeypots are scattered throughout the form, and made invisible to humans through CSS.

Once the form data is submitted, valdation occurs to detect whether a bot was present:

  1. The spinner gets read to figure out which form fields match which data.
  2. The timestamp is checked and rejected if it's in too far into the past, in the future, or not present.
  3. The spinner value is checked to ensure it hasn't been tampered with.
  4. Honeypots are checked to see if data is provided in any of them.
  5. The rest of the data is validated as usual.

Ned's article goes further into the details than I have above, so I highly recommend reading it if you're interested in this kind of thing. Time will tell as to whether this technique will be successful at keeping bad comments out, but I'm optimistic that it will.

Dumping WordPress

Dec 17, 2020

Today is the day I dump WordPress for a custom-built, Django-powered platform. The site look and feel has changed, so click through from your RSS reader (if you're reading from one) to see the new look. In addition, comments are now enabled on all posts. I'm using an interesting spam mitigation technique that will hopefully keep things clean. Time will tell how well it works.

I made a few design decisions with this redesign and with the platform itself:

  1. Site search is now simply powered by Google. They do search better than most anyone around, so why reinvent the wheel?
  2. Posts and pages are Markdown-aware by default. Comments accept Markdown too!
  3. I'm using off-the-shelf components from Bootstrap, as well as icons from FontAwesome.
  4. Things should, generally, be responsive and pleasing on mobile.

Working with Django is, generally, a real pleasure. I was able to stand up this platform in just under three weeks time:

Commit graph for this site's platform

If you spot any issues, please let me know; bug reports are a big help. I'm sure there are broken images here and there, especially on some of my older posts. Additional details on this new platform are coming soon, including a pointer to a great article on spam mitigation. I'm hopeful that I will get back into a more frequent writing cadence now that I have a better platform to use.

I Hate WordPress

Dec 12, 2020

The longer I spend with WordPress, the more I loathe it. This site has been steadily chugging along since 2004 (16 years as of this writing!) with the Powered by WordPress brand stamped on each page. In that amount of time, WordPress has become an unwieldy behemoth of a platform. It's honestly feeling more and more like Microsoft FrontPage to me (remember FrontPage?). Everyone should apparently design websites in the WYSIWYG editing style. As hopefully everyone knows, however, that never seems to end well.

Apart from having to update the core software every few weeks to plug the latest security holes, I find the plugin support to be hit or miss. All the higher quality plugins tend to be paid pieces of software, some even requiring monthly subscription fees. Free plugins generally fall into two camps:

  1. They're outdated and unsupported
  2. They're poorly designed/documented

Lots of the functionality I'm interested in — image galleries, Markdown support, spam mitigation, bulk post management, the list goes on — isn't available natively. If it is, the implementation is generally half-baked. Keeping this site running has become a chore as WordPress has gotten larger.

Thankfully, this is where being a professional web developer pays off.

I'm in the process of designing a brand new, custom platform to power this site. It will be powered by Django, and will have every feature I care about baked in. I've been hacking on it solidly for the past two weeks, and I estimate another week or so before it's able to launch. Along with it will come a small visual refresh. I'm excited about these changes, and I look forward to sharing them soon.

Another great philosophical video from Essential Craftsman. If you know someone of an impressionable age, be sure to share it with them.

Railway Maintenance

Sep 22, 2020

I love trains, and I love mechanical automation. This really great video is an intersection between the two, and explains the process used to replace rail on an active railroad. Fascinating stuff!

During these COVID-19 lockdown times of ours, I've been thinking about the interesting research opportunities this strange occurrence must be affording scientists around the globe. For example, in the days following the 9/11 terrorist attacks on New York, weather scientists had a unique chance to study the skies without the influence of aircraft contrails. Certainly those kinds of studies can be done now, since air travel has been greatly reduced. I wonder what other branches of science this shutdown is helping. Are air-pollution studies easier to conduct? Can certain types of infrastructure examinations be completed more easily, without the burden of traffic and congestion? How is electricity use being affected? It's an interesting line of thought to ponder.

This line of thinking, however, goes in a darker direction as well. How many more cases of depression will result from these upcoming months of isolation? Will suicides increase? What about divorce rates? How will children in school be impacted in terms of what they learn? What about their social development?

My daughter, who is younger than 2 years of age, is reaching a critical point in her development as a child. Her isolation from both family members and other children will certainly have a negative impact, at least in the short term. What kind of long term effects will it have? I guess only time will tell.

Marbula One

Mar 2, 2020

I was recently (and randomly) recommended the following video on YouTube, in which marbles race in a Formula One format on surprisingly well constructed tracks. This is the first "race" of the 2020 "season," complete with podiums at the end of each race, "yellow-marble" track cautions (at least one occurs in race 2), and a season-long point system for each team.

I cannot stress how ridiculous this all is, but it's oddly satisfying to watch. Also note that the first 60 seconds of each video can be skipped (the video below starts at the 1:00 mark for your convenience).

Note: I now have an improved version of this script.

I use Python virtual environments a bunch at work, and this morning I finally put together a small helper script, saved as a Gist at GitHub, that makes enabling and disabling virtual environments a lot easier. I'm not sure why I didn't do this a lot earlier. Simply type work to enable the virtual environment, and work off to disable it. This script should be in your PATH, if it's not already obvious.

Here's the script itself:

@echo off

if exist "%cd%\venv" (
    if "%1" == "off" (
        echo Deactivating virtual environment
        call "%cd%\venv\Scripts\deactivate.bat"
        echo.
    ) else (
        echo Activating virtual environment
        call "%cd%\venv\Scripts\activate.bat"
    )
) else (
    echo No venv folder found in %cd%.
)

Stopping CVS Robocalls

Jan 23, 2020

In the past few weeks, I've received a couple of calls each day from CVS Pharmacy. I'm not sure what has changed on their end to cause these calls to start, but it has gotten annoying enough that I did some searching on how to disable them. Here's how to do it:

  1. Call 1-800-SHOP-CVS (1-800-746-7287)
  2. Say "More choices" (you may have to say this twice to get to the right menu; I did)
  3. Say "Calls"
  4. The system will confirm your phone number
  5. Say "Stop"
  6. Say "Correct"

How to Build a House

Jan 21, 2020

I watch far more YouTube these days than I do actual television. A series I just started is How to Build a House from the Essential Craftsman channel. In this series (that is still being released, as of this writing), they walk through the entire process of the house building process. From doing due diligence on the lot itself, to getting the building permit; from preparing the concrete forms to actually selecting the type of concrete (who knew there were different types?). I'm only about 15 or so episodes in, but it's been really eye opening in terms of the amount of work that actually goes into building a house.

Each episode teaches you something new, which has been a lot of fun. I recommend this series if you're a fan of This Old House or if you have an interest in how things are made. Here's the first video:

Simplicity in Design

Jan 16, 2020

One of the best things about picking up woodworking as a hobby has been the learning process. Every project has taught me a number of new things, whether it's a new tool, technique, or construction method. My latest project was a kitchen bench which I designed myself using Sketchup Make 2017. One of the lessons this project taught me was not to over-complicate a design.

The following images show two designs for the leg assembly for the bench. The first design is what I built; the second is a simplified variant.

The first design made attaching the bench top more difficult than it should have been at the ends. It also complicated the finishing process, and resulted in the purchase of additional material (4/4 poplar, in this case, rather than the 8/4 I used elsewhere in the frame). I only discovered this after building the first design, of course, but that's all part of the learning process. My intention with design 1 was to improve the look and feel of the ends of the frame, but the irony is that this portion is nearly invisible with the top in place.

As I design future projects, I now know to take issues like these into account.

I stumbled on this video over the Christmas break, and I've watched it several times now. Lots of good advice in this video on being more productive, and how working both smarter and harder, is a key to success.