My Trello Weekly Summary

I’m trying to get myself to better recognize work I’ve completed, so I wrote a process to look at my Trello board and send a Slack message to myself. This is why and how.

I used to write a lot about Trello.  The API, how I use it, how my day-job teams were using it.  It’s been five years since I’ve said anything new.

A big part of that is that I haven’t been doing anything new.  I did a ton of Trello-related work in about a one-year period and then was done.  But I still use it every day and recently found a gap that I decided to close.

My personal Trello board has three columns.  “Not Started,” “In Progress,” and “Done.”  Pretty simple.

The “Done” column is the one I decided needed some help.

The “Done” column is important to me.  It’d be easy to not have one, simply archiving cards as they were completed.  I think it’s easy to get lost in a never-ending “Not Started” list without seeing the things that have been completed.  Instead, I have a process that runs nightly to archive cards over two months old, after they’ve had time to pass out of my mind.

I’ve been finding of late, though, that that’s not working for me.  I see the “Done” column, I know it’s there and that there’s a lot of stuff in it, but that’s what I’ve boiled it down to.  “A lot of stuff” that’s done and easy to ignore.

So I decided to add another automated process.  This one runs weekly and sends me a message via Slack, detailing how many cards I’ve completed in the last week.

I’m not doing this for analytics or anything.  I don’t have any goals to accomplish a certain number of tasks (they’re not estimated or anything so they’re all different sizes, anyway).  I just want to get another reminder in front of my face.  This one is a little disruptive, so maybe it’ll stick differently.


For the record, the following is the code for this new process (with a little obfuscation):

<?php
use cjrasmussen\SlackApi\SlackApi;
use cjrasmussen\TrelloApi\TrelloApi;

set_time_limit(120);

// ALLOW FOR SPECIFYING A TIME TO RUN FROM VIA THE COMMAND LINE
$time = null;
$options = getopt('', ['time:']);
extract($options, EXTR_OVERWRITE);

if (is_numeric($time)) {
	$check_time = (int)$time;
} elseif ($time) {
	$check_time = strtotime($time);
} else {
	$check_time = time();
}

$back_time = strtotime('-1 week', $check_time);

$trello = new TrelloApi($trello_key, $trello_secret);

// GET THE CARD DATA FOR THE COMPLETED LIST
$data = $trello->request('GET', ('/1/lists/' . $list_id . '/cards'));

$completed = [];
foreach ($data AS $card) {
	$recent_actions = $trello->request('GET',
		('/1/cards/' . $card->id . '/actions?filter=all&since=' . date('c', $back_time) . '&limit=1'));

	if (count($recent_actions)) {
		$start_timestamp = time();
		$all_actions = $trello->request('GET', ('/1/cards/' . $card->id . '/actions?filter=all'));
		foreach ($all_actions AS $action) {
			$timestamp = strtotime($action->date);
			if ($timestamp < $start_timestamp) {
				$start_timestamp = $timestamp;
			}
		}

		$labels = [];
		foreach ($card->labels AS $label) {
			$labels[] = $label->name;
		}

		$completed[] = [
			'title' => $card->name,
			'url' => $card->url,
			'labels' => $labels,
			'start_timestamp' => $start_timestamp,
			'end_timestamp' => strtotime($recent_actions[0]->date),
		];
	}
}

$blocks = [];
$blocks[] = (object)[
	'type' => 'header',
	'text' => (object)[
		'type' => 'plain_text',
		'text' => count($completed) . ' item' . ((count($completed) === 1) ? '' : 's') . ' completed in the last week',
	],
];

foreach ($completed AS $item) {
	$blocks[] = (object)[
		'type' => 'divider',
	];
	$blocks[] = (object)[
		'type' => 'section',
		'text' => (object)[
			'type' => 'mrkdwn',
			'text' => '<' . $item['url'] . '|' . $item['title'] . '>',
		],
	];
	$blocks[] = (object)[
		'type' => 'context',
		'elements' => [
			(object)[
				'type' => 'mrkdwn',
				'text' => '*Labels:* ' . implode(', ', $item['labels']),
			],
			(object)[
				'type' => 'mrkdwn',
				'text' => '*Started:* ' . date('n/j/Y g:i A', $item['start_timestamp']),
			],
			(object)[
				'type' => 'mrkdwn',
				'text' => '*Completed:* ' . date('n/j/Y g:i A', $item['end_timestamp']),
			],
		],
	];
}

$msg = [
	'blocks' => $blocks,
];
(new SlackApi($_ENV['config']['slack']['debug_webhook']))->sendMessage($msg);

This makes use of my TrelloApi and SlackApi helper classes.

DetroitHockey.Net 25th Season Logo Design

A look at DetroitHockey.Net’s 25th Season logo on the site’s 24th birthday.

Today marks the 24th birthday of DetroitHockey.Net, my Detroit Red Wings-centric hockey site and the first site I ever created.

Normally the 24th birthday would be the start of the 25th season but in the world of COVID, timelines aren’t what they once were.  Nonetheless, earlier today I unveiled a 25th Season logo at DH.N.

The commemorative 25th Season logo for DetroitHockey.Net

I’d been doodling quite a bit in the lead-up to the site’s 24th birthday.  I knew I wanted to differentiate the 25th Season logo from the 20th Season logo by putting the “25” front and center, rather than the site logo, but I had a hard time with anything beyond that.

I was trying to avoid using a bounding shape and was focusing on just using “25” and the site logo when I realized I couldn’t make those work the way I wanted to.  I didn’t want to do a shield (the DH.N logo) inside of another shield, so I went with a circle as the bounding shape.  I felt like the circle needed to be broken up to help distinguish the anniversary logo from the site’s “promotional” logo, so the ribbon came in and the site logo was lowered to break the bounding circle at the bottom.

The ribbon gave a good place for most of the anniversary-related text.  Like it is in the site’s “promotional” logo, “DetroitHockey.Net” arches across the top part of the bounding circle.

The four stars across the bottom part of the logo represent the four Stanley Cup Championships the Red Wings have won since the site was founded in 1996.

My one complaint about this is that it feels like there is empty space before and after the “DetroitHockey.Net” text.  It’s necessary to have some space to let the individual elements breathe but that feels like just a little too much and I couldn’t figure out a better way to handle it.

I’m a Bad Citizen of the Open Source Community

On how I learn, how I code, and not sharing well with others.

I’ve always said that I love the open source community because of the opportunity it provides for riffing off of each other.  I’ve also acknowledged that I have the tendency to re-invent the wheel.  If those seem at odds with each other, it’s because they kind of are.

How that usually works for me is that I’ll Google to see if someone has solved the problem I’m working on and, if so, I’ll learn what I can from what they did and re-work it to fit my specific situation.  Then maybe I’ll write about it and embed my code in the blog post.

I do this because I’ve always wanted ideas, not code, from other people.  I don’t want to composer install their stuff, I want to see how they did it and then do it myself.  So when it comes time for me to share, I write about it but don’t make my code particularly accessible.

There are two problems here.  One is that my way isn’t particularly efficient.  I need to be better about using other people’s code and not reinventing the wheel.  The other is that I’m not sharing well because my code stays locked up in my own repos.

Not sharing.  Mrs. Biondolillo would be disappointed in me.

I realized this as I was trying to consolidate a bunch of my code into a framework package.  In doing so, I found that I have a bunch of one-off tools and classes.  Things that I don’t use much and didn’t bother to build tests for or anything but that might benefit others a bit.  And I never put them out there.

So I’m going to start dumping some of these out to GitHub, starting with the current version of my Trello API helper class.  If no one uses them, that’s fine.  At least they’ll be out there to find out.  And I’ll start being a better citizen of the open source community.