University CompSci Plagiarism

I’ve worked really hard on django-todo over the years, so was very dismayed to receive email recently from a CompSci student at the University of Western Australia informing me that the department had taken the django-todo source code, removed the license file and all attribution, and included its code in one of their assignments.

Of course I’m all for open source software being used for educational purposes, and would have been honored and pleased if they had done this in an OSS-respecting manner, but they chose to take the legally shady route instead, and to leave students with the impression that this was their original code. This is not how we educate students to become productive, respectful members of the open source software world.

The offending distribution is linked to from the CITS5501 Software Testing and Quality Assurance page – download the file

I do hope the university takes some form of disciplinary action against the professor, who obviously knows better. And I hope the professor does the right thing. In case it’s not clear what “the right thing” is, I’ll spell it out: Restore the license file, and restore attribution from the files you removed it from. Better yet, save yourself a bunch of work and don’t include a copy of the software in your assignment download at all – just link to the github repo! That’s what it’s there for.

Ambient Humanity – Notes on a Month Without Facebook

A month ago, I decided to take a mental health break from Facebook. Between the Cambridge Analytica mess, the cognitive pressures of always trying to juggle half a dozen ongoing conversations, and a creeping sense of “opinion exhaustion” (tired of my own and everyone else’s too), something had to give.

Alamere Falls / Coastal Trail
Winter rains pouring over the cliff into the sand at Alamere Falls, Pt. Reyes

The timing was good, and necessary. Over the past month, I’ve gone through one of the most intense work-stress periods of my life, so leftover mental energy was at a premium. When I did have time to unwind, I replaced the usual Facebook time with attention to platforms I knew were great (but very different) – Quora, Reddit, Twitter, and an endless supply of news and blogs aggregated via Feedly. All of them were interesting in their own ways, I was missing something crucial – what Kottke calls “ambient humanity“:

It is psychological gravity, not technical inertia, however, that is the greater force against the open web. Human beings are social animals and centralized social media like Twitter and Facebook provide a powerful sense of ambient humanity — the feeling that “others are here” — that is often missing when one writes on one’s own site. Facebook has a whole team of Ph.D.s in social psychology finding ways to increase that feeling of ambient humanity and thus increase your usage of their service.

Kind of hard to put my finger on why things feel so different outside of the Facebook bubble. Sure there are a zillion people having interesting conversations on other platforms, and I’m more than comfortable jumping in a pool with strangers, but the discussions are like drive-bys — I don’t feel invested the same way I do with people on Facebook. There’s something qualitatively different about talking things through with people you know or have known, compared to leaving one of a thousand comments on a topic with a bunch of random humans.

And there’s a level of civility on Facebook that I am not finding anywhere else. We are, for the most part, polite with people we actually know, and sadly, way too often rude with strangers.

Then there’s the news aggregation aspect. I found interesting content everywhere, of course, and wandered into great threads all over the place, but haven’t been able to shake the feeling that Facebook is not only great at social, it’s also among the best at news aggregation. Twitter is too noisy, even with tuning. Quora really isn’t about news. Reddit can be about news if you use it that way, but I find it much better for surfacing random stuff than for seeing what’s going on the world today/now. A dedicated RSS reader (I use Feedly) is right up there with Facebook, but feels “cold” in comparison.

But wasn’t this exactly what I was looking for — a break from the continual pressure to engage? If I so much as look at Facebook, I’ll post or comment. And when someone responds, I’m obliged to return and continue the conversation (you can’t just ignore a friend when they’re talking, right?). Outside of Facebook, that pressure vanishes. It’s exactly what I wanted, but it felt… empty somehow. The “ambient humanity” was missing.

There was a huge benefit to getting out of the Facebook bubble for a while — I felt and became a calmer version of myself, which allowed me to be more focused on the work stuff. There’s something about Facebook that seems to amplify outrage. “If you’re not outraged, you’re not paying attention.” Now more true than ever, and Facebook does make me feel more “in touch” than any other platform, but here’s the thing: Outrage is a drug. However justified your outrage may be, however much this messed-up world is pissing you off, this remains true: Start down the outrage path, and pretty soon you’re subconsciously looking for things to be outraged by, taking cues from your tribe on the daily outrages, dwelling on outrage, going to sleep and waking up with outrage.

Miles and Mardo, Frozen Shady Oak
Winter lake, Minnesota 2018

The paradox is that if you want to stay aware, you’re going to be outraged. But continuous outrage is a super-unhealthy state of being. Ditch Facebook for a while and tell me you don’t feel the difference. It’s lovely to check out of that hotel for a while.

I had intended to do a lot more blogging this month, but didn’t, because you know, work. I still want to. But paradoxically, I find myself longing for this month to be over so I can return to Facebook. Weird, right?

Announcing django-todo 2.0

django-todo is a pluggable, multi-user, multi-group, multi-list todo and ticketing system – a reusable app designed to be dropped into any existing Django project. Users can create tasks for themselves or for others, or create ”assigned tasks” that will be filed into a specific list (public tickets).

That was the original project description, and it hasn’t changed in 10+ years.

When I first created django-todo, it was a simple “let’s learn Django” project I gave to myself. I open sourced it, it’s been relatively successful, and the project has received numerous contributions over the years (grateful!). When I heard that it wasn’t compatible with Django 2.0, I looked back on that old code and realized it was time for a major refactor/upgrade. I’ve been working on the update for the past couple of months (evenings only).

Virtually every module and template has been refactored, much more in line with current best practices. The update started small, but by the end, I had made 75 commits and written the first suite of working tests (finally!). And I adopted Bootstrap as the default layout engine. And finally got around to creating a live demo site for the project.

django-todo 2.0 requires Django 2.0 and Python 3.x – no apologies. Unfortunately, this is a backwards-incompatible update (you’ll need to migrate old data manually, if you have any).

Hope it’s useful to a few teams or individuals out there. Contributions still very much welcome.


Ello was founded as a social network devoted to never selling or sharing your data. Diaspora was founded as a social network devoted to decentralization so no one could “own” your data. Google Plus offered a better experience than Facebook in dozens of ways. There have been heaps of Facebook alternatives over the years, and they’re all virtual ghost towns. Why can’t any of them succeed? Because Facebook has one feature no one else can replicate: ALL OF YOUR FRIENDS ARE HERE. Now, with many of us fed up and looking for alternatives, I’m asking: How can we break FB’s monopoly on ALL YOUR FRIENDS ARE HERE? Until we do, the number of people who truly leave the platform will be close to zero.

Space Oddity

Easy to laugh this off or to get pizzy about space junk, but cannot underestimate the significance of what happened today – humans launched a Tesla into space blasting Bowie on infinite repeat, using a rocket many times larger than anything we’ve launched for decades, for a fraction the cost of what NASA launches cost, *and* brought two out of three of its booster rockets back for a perfect (and simultaneous!) landing, ready for re-use. And we got a view from the Tesla-in-space streamed live on YouTube as it all went down. Humans are awesome, and Mars just got a whole lot closer.

Great pics in the Guardian coverage.

Alexa, Shut Up!

Listening to a mother talking about how her 11-yr-old daughter was rude to Amazon Alexa, yelling “Shut up!” at it. Mother admonished her for being rude. Child: “Gosh mom, it’s just a robot, it’s not like it’s an AI or something.” In other words, child makes clear distinction that AI has “real” emotions and that we shouldn’t hurt its feelings, while a simple robot does not. Amazing times.

A Nanosecond Per Foot

It takes light takes about a nanosecond to travel a foot. So if you’re sitting four feet away from me, I’m seeing you as you were 4 billionths of a second ago. Meanwhile, Earth light takes four years to get to Alpha Centauri. So for anyone on a planet in Alpha Centauri watching Earth TV, it would appear that we still have an adult president in office.

Testing Reusable Django Apps with Pytest

Django project dependencies are normally installed with a simple

pip install myapp

But if you want to be a code contributor to a dependency such as a reusable Django app (or if you are writing one), the usual pattern is to:

  1. git clone that app to another directory, outside of your project
  2. From within your project dir, use
    pip install -e /path/to/the/clone

This creates a foo.pth link and/or a foo.egg-link in your project’s virtualenv, rather than copying the code into the project itself, so you can work on the separate repo and have changes reflected in your project in real time.

That’s all well and good, but if you use pytest and need to run tests in the dependency, just running pytest from your project dir won’t find tests that live in the dependency. So can’t you just run pytest from within the dependency’s directory? Not quite – since it’s a Django app, it’s going to assume access to Django itself, and likely to other apps installed in the Django project, such as users. It would be redundant and cumbersome to install Django in the dependency’s directory, and that still wouldn’t solve the problem of access to users etc.

I can’t seem to find mention anywhere of a “best-practice” solution to this dilemma, but after much futzing around and feeling like I must be missing something obvious I tried what should have been plain from the outset:

  1. From the project dir, pip uninstall myapp
  2. From the project dir, create a symlink:
    ln -s /path/to/the/clone myapp
  3. Add myapp to your .gitignore

Now the separate reusable app appears to Django and to pytest like it’s present in the project, while maintaining physical separation between the project and the dependency. Tests run, and there’s no confusion. Just be sure to .gitignore the symlink.

There’s a gotcha to look out for here: Typically, a reusable app called “myapp” will live in a parent folder that’s also called “myapp”. If you make your symlink to the top-level folder, the imports inside it will not work. You’ll want to create your symlink to the inner folder, e.g.:

ln -s /Users/yourname/dev/myapp/myapp .

Unless I’m mistaken about something, a simple symlink (rather than pip install -e appears to be the easiest way to organize the relationship.

How To Screw Up a Good Backup System

I back up our family computers like crazy, using a combination of Time Machine and cloud backup via Backblaze. But I did something dumb and almost lost our family’s entire history of home videos. Facing estimates of $500 – $1500 for professional data recovery, I stumbled on an awesome hack that saved the day.


All of our computers’ internal hard drives get a dedicated external Time Machine, and we use Backblaze for extra insurance, so our data is safe in the cloud in case of fire, theft or flood. But we also have a few external hard drives that store things like large music collections and our home videos. The external drives back up to Backblaze only (no Time Machine).

All of that has been working hunky dory for years, and I felt confident we were safe. Then, a week ago, I realized that the drive that stores our family videos (“Gorgonzola”) would no longer mount, with any cable, on any of our machines. Yikes! So I turned to Backblaze for a restore, only to find it wasn’t showing up there either! Double yikes, freakout.

What Happened

At some point in the distant past, I did something dumb, though I didn’t realize it was dumb at the time – I realized I had lots of extra space on Gorgonzola and decided to let that drive do double-duty, as a Time Machine drive for a laptop. What I didn’t take into account was the fact that Backblaze has a reasonable rule – they don’t back up your backups. So when Backblaze detected that Gorgonzola was now a Time Machine drive, it dropped it from the manifest. I never noticed it had been dropped.

So now I’d realized that I had NO backup available anywhere for this precious, unreplaceable data. So I called some data recovery services, and got estimates ranging from $500 – $1500. The data was important enough to me that I’d pay that ransom, if it came to it. But of course I didn’t want to.

So Crazy It Just Might Work

The next day, I stumbled on a brilliant suggestion: Often, when a drive won’t mount, it’s because the USB controller circuit board inside the drive case has gone south, and that the drive itself is fine. Solution: Purchase an identical drive, take them both apart, and swap the controller chips. Brilliant! Found an identical drive on Amazon for $100 (Seagate Backup Plus Slim), and went for it.

Prying the case open and removing the drives turned out to be easier than expected (YouTube video), and the controller chips slipped off easily. Quick swaperoo, and lo and behold, it worked! Gorgonzola showed up as normal, and I’m rescuing my data right now. Of course, both drives were destroyed in the process, but at this point, I don’t care.

Yay, internet.

Django template tag to display latest git commit, date and tag in template

I found a few references to bits and pieces of this in various places, but this snippet ties all three bits of information together in a single string, and accounts for calling the path to the git repo from anywhere (which tends to work “magically” on localhost but breaks on servers).

If you don’t use git tags, season to taste.

def git_ver():
    Retrieve and return the latest git commit hash ID and tag as a dict.

    git_dir = os.path.dirname(settings.BASE_DIR)

        # Date and hash ID
        head = subprocess.Popen(
            "git -C {dir} log -1 --pretty=format:\"%h on %cd\" --date=short".format(dir=git_dir),
            shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        version = head.stdout.readline().strip().decode('utf-8')

        # Latest tag
        head = subprocess.Popen(
            "git -C {dir} describe --tags $(git -C {dir} rev-list --tags --max-count=1)".format(dir=git_dir),
            shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        latest_tag = head.stdout.readline().strip().decode('utf-8')

        git_string = "{v}, {t}".format(v=version, t=latest_tag)
        git_string = u'unknown'

    return git_string

Then, in your template, you can simply:

{% if user.is_superuser %}{% git_ver %}{% endif %}