Categories
Uncategorized

23 Years

I just passed the 23rd anniversary of creating my first website, which I would eventually name Midnight on a Wednesday. For my 16th birthday, my mom let me register my first domain name, kjcoop.com.

I was exposed to computers late relative to other people of my generation. I thought it was amazing that I could read the writings of people all over the world – there were no boundaries on the internet. This was before WordPress and Facebook and even Friendster. if you wanted a website, you had to learn HTML.

Before kjcoop.com, my website lived at http://geocities.com/athens/agora/4213. Geocities allowed you to FTP files or edit the HTML in a textarea. I tweaked the contents of the placeholder page in the textarea and figured out HTML as I went. Frames were a big deal in those days – it was how a non-programmer got the same content on each page without having to duplicate a bunch of content on each file. My browser couldn’t display frames.

I discovered CSS around 2000. With it, you could take the underlines off links. You didn’t need Javascript to change a hover color. Amazing!

The thing about the front-end is that you have to know what the visitor’s browser will support, and every version of every browser is a little different. This was the tail end of the browser wars. I will forever carry a grudge against IE 5.

I didn’t bother learning Javascript. I didn’t see the point when users could just turn it off. Boy did I bet on the wrong horse.

My interest in the front-end tapered off around 2010. I tried to pick up some CSS 3 recently but these days it’s all dark magic to me. CSS is now Turing complete.

I taught myself a little PHP. In 2002 I took a class in Java, I want to say we used 1.2. The compiler came on a CD in the back of the book. I didn’t get enough background to build anything cool with it – not that I didn’t try.

In 2003 I went to Humboldt State and took some Computer Science classes wherein I learned C++. It gave me enough background to make progress as a PHP developer. In 2004 or 2005, I got a job at Humboldt’s help desk. Though help desk and programmer are different career tracks, my time at the Help Desk was a foundational step in becoming a well-rounded nerd.

Early Computer Science courses, at least then and there, are about how computers are engineered. Programming is taught as a tool to study larger computer science concepts. I didn’t want to engineer hardware, I wanted to program.

Around the time I dropped out in 2006, Ruby on Rails was the new hotness. I watched a video on it and was exposed to Active Record for the first time. It inspired me to being working on my own framework. In retrospect, I can appreciate that it was a hot mess.


They say you can’t forget where you come from. In a lot of respects, I was born twenty-three years ago in a textarea.

I’ve always thought a tombstone that describes me as beloved spouse and parent is about worst-case scenario. I don’t want my whole life to be boiled down to just having been somebody’s relation. I decided a few years ago, I’d be happy with “Beloved spouse, parent and programmer”. Then, at the very bottom, “</textarea>”.

Categories
Asana Productivity Todoist Trello Zenkit

Comparing Trello, Asana, Zenkit and Todoist

These are the four platforms where I have data. I hope to one day move it to a single platform.

Kanban uses the term card, Todoist says task. I use both.

AsanaTodoistTrelloZenkit
Beauty5/10 – has a few backgrounds to choose from. Images attached to cards become small thumbnails.9/10 – An ever-growing number of pretty backgrounds thanks to Unsplash. Attached images become medium thumbnails on corresponding cards.5/10 – has a few backgrounds to choose from. Images attached to cards become small thumbnails.
TagsColored background and globalColored font and global; paidColored background and siloed per boardColored background and siloed per board; can have multiple categories. Can be used to color the whole card. See below.
Descriptions☑️☑️☑️
AttachmentsCan be attached to cards or commentsCan only be attached to comments; paidCan be attached to cards or commentsCan be attached to cards or comments
Repeating tasks☑️☑️❌ – Power-up. Appears on due day.☑️
Reminders☑️☑️☑️ – Added as a separate field, but you can add many such fields
Links between cards/boardsPaste the URL yourself.Paste the URL yourself.☑️ – Add it as an attachment. No URL pasting requiredPaste the URL yourself.

Zenkit does really interesting things with tags that I think are worth addressing in greater depth. You can have different batches of tags. For example, each task might want a group of tags that refer to its location, then a separate group of tags to represent times of day.

It doesn’t make a distinction between a list heading and a category, so you can see the tasks organized by any batch of tags. Let’s expound on the list above with the two batches of tags. Suppose your headings are the classic to-do, doing and done. You created the two batches of tags described above, but the column heads are a third batch. By default, I walk in and see the do-doing-done columns. I can choose a filter and organize it by time of day, then the do/doing/done columns will appear on the cards like any other tag.

Another interesting thing it does with tags is that it’ll let you define a batch that will color the task itself. Again, suppose you want your morning tasks red, midday, orange, evening, yellow, and night, green. There’s a setting where you select which batch of tasks you want to define the color of the cards. If there are multiple tags from that field applied (say something can be done morning or midday), it guesses which color you want.

I think Zenkit is the most interesting, but it’s missing global tags. I recently turned my attention from it to Todoist, which is a little like going from a luxury car to a compact sedan. I may be able to split the difference with something like Zapier or IFTTT.

Asana also has global tags, but there are a handful of features the other three have that it lacks. The one that comes to mind is that you can’t copy lists to other boards, you can only copy whole boards.

The reason I’m so hung up on this global tags business is that I always have like 30 boards going. If I have a couple hours off work during the middle of conventional working hours, I want to easily pull up a list of all the things that can only be done during hours I’m normally working.

Categories
PHP Programming Todoist Zenkit

Zenkit API to Todoist CSV

I’m of two minds about posting this. The code is ugly. I’m posting it anyway because there aren’t a lot of resource for this this and was kind of a bear. I hope to save somebody else some trouble.

As is well-documented, I’m a fan of productivity tools. Todoist added boards to support a kanban format and let me tell you, I am here for it.

My productivity software journey of the past couple years goes like this: Trello and Todoist -> Asana -> Trello alone -> Zenkit -> Todoist alone. I have data living in all four places. I’m trying to move as much as I can to Todoist (it has global tags which Trello lacks, but its missing some other features I’m pretty fond of). Definitely Todoist will last forever and I won’t come running back to Trello.

Don’t forget to put in your API key.

<?php
/*
 * Create a Todoist CSV file from the Zenkit API. Resulting file requires some
 * manual work.
 *
 * @license: GPL
 * @author KJ Coop
 *
 * Zenkit is more complicated then Todoist, so we kind of have to wedge things
 * in. For example, Zenkit only has groups of labels, and you decide which ones
 * you want to use a section heads at any given moment. Consequently, when
 * Zenkit hands off its data, it's not easy to see which labels Todoist should
 * use as section heads and which it should use as labels. It's probably
 * possible to detect which labels are grouped together in the Zenkit output,
 * but I haven't put any effort into it (yet?).
 *
 * In Todoist, to add a label, you append the title with @Desired_Label. Because
 * we don't know whether we want Todoist to show any given label as a label or
 * a section head, it appends all the label-like strings as labels.
 * Consequently, if you want to keep your kanban format and of course you do or
 * what is even the point of being alive, you have to open up the finished
 * CSV file an manually manipulate it.
 *
 * @todo Things I haven't put any work into:
 *    - Descriptions - Zenkit has them whereas Todoist does not
 *    - Attachments - in Zenkit, they can be on a file. In Todoist, they're
 *      necessarily in a comment
 *    - Comments
 *    - Users
 *
 *
 *
 * Links:
 *    - https://todoist.com/help/articles/how-to-format-your-csv-file-so-you-can-import-it-into-todoist
 *    - https://base.zenkit.com/docs/api/overview/introduction
 *    - https://packagist.org/packages/idoit/zenkit-api-client
 */

require_once(__DIR__ . "/vendor/autoload.php");

use idoit\zenkit\API;

function appendToCsv($filename, $type, $content) {

    // https://todoist.com/help/articles/how-to-format-your-csv-file-so-you-can-import-it-into-todoist
    $array = [
        $type,
        $content,
        '4', // Priority
        '1', // Indent
        '',  // Author
        '',  // Responsible
        '',  // Date
        '',  // Date lang
        '',  // Timezone

    ];


    $csv_file = __DIR__.'/'.$filename.'.csv';

    // https://www.geeksforgeeks.org/how-to-convert-an-array-to-csv-file-in-php/
    $fp = fopen($csv_file, 'a');

    // Loop through file pointer and a line
    fputcsv($fp, $array);

    fclose($fp);
}



function listWorkspacesAndBoards() {
    $apiKey = '';

    $api = new API($apiKey);

    $count = 0;

    // Some of the arrays Zenkit returns are like this and we can ignore them.
    $emptyArray = [
        'text' => '',
        'searchText' => '',
        'textType' => 'plain'
    ];

    // Output data as array - workspaces and their collections/lists.
    $workspaces = $api->getWorkspaceService()->getAllWorkspacesAndLists();
    foreach ($workspaces as $key => $workspace) {
        $filename = $workspace->id;

        foreach ($workspace->lists as $list_key => $list_item) {
            $elements = $api->getElementService()
                ->getElementsInList($list_item->shortId);

            $entries = $api->getEntryService()
                ->setElementConfiguration($elements)
                ->getEntriesForListView($list_item->shortId);
            //    ->getEntry($list_item->shortId, 1);

            $listEntries = $entries->listEntries;

            $distinctTitles = [];
            $distinctWholeStrings = [];

            if (!is_array($listEntries)) {
                continue;
            }

            foreach ($listEntries as $entry) {
                if (isset($entry->displayString)) {
                    $title = '';

                    foreach ($entry->elements as $element) {
                        /*
                            Don't reset the title at each iteration of this
                            loop. We append labels in subsequent iterations.
                            Fesetting the title resets the whole task. Consider:
                            In Zenkit, you have something like the following:
                                Task 1
                                    * Label A
                                    * Label B
                                Task 2

                            The best sense of the API I can get is that it gives
                            you:
                                Task 1
                                Label A
                                Label B
                                Task 2

                            The code can see that Task 1 is a task and that the
                            labels are a different kind of string that relates
                            to the last thing it encountered. If doesn't know
                            until Task 2 comes along that it's finished with
                            Task 1. Until it sees a task, it has to keep
                            appending tasks to the last title it encountered.
                        */

                        if (isset($element['text']) && $element['text'] != '' && !strpos($element['text'], 'http')) {
                            // Remember that this is a title
                            $title = $element['text'];
                            $distinctTitles[md5($element['text'])] = $element['text'];

                        } else if (isset($element['categoriesSort']) && isset($element['categoriesSort'])) {
                            foreach ($element['categoriesSort'] as $cat) {
                                if (isset($distinctTitles[md5($title)])) {
                                    /*
                                        Add categories as labels. This is an not
                                        ideal map - Zenkit sees no distinction
                                        between labels and column heads,
                                        wbereas Todoist does. We add them all as
                                        labels if you want to change it to a
                                        section head, you must alter the
                                        resulting CSV manually :(
                                    */
                                    $distinctTitles[md5($title)] .= ' @'.str_replace(' ', '_', $cat->name);
                                }
                            }
                        } else if ($element == $emptyArray) {
                            // Silence it
                        } else if (isset($element['persons'])) {
                            // Silence it - I'm the only person who matters, obviously
                        } else if (isset($element['files']) && isset($element['filesData'])) {
                            // Not worried about files right now.
                        } else {
                            // IDK LOL OMG
                        }

                        // Remember the whole string.
                        $distinctWholeStrings[md5($title)] = $title;

                    }
                }
            }
        }
    }

    echo "Distinct Titles: \n";
    print_r($distinctTitles);

    foreach ($distinctTitles as $title) {
        appendToCsv($filename, 'task', $title);
    }
}

try {
    listWorkspacesAndBoards();
    echo "\n";
} catch (idoit\zenkit\BadResponseException $e) {
    echo 'Exception! The status was ' . $e->getCode() . ', response: ' . $e->getResponse()->getBody()->getContents();
} catch (Exception $e) {
    echo 'Exception! Something else went wrong: ' . $e->getMessage();
}

Categories
Bash CLI Linux

Check a Domain Name for SSL Problems

Today I’m highlighting a script I wrote that will check sites for SSL problems. You can hand it a file with hard-coded domains, or you can pass domains in as arguments. I wrote a complementary script that will find all the vhosts on the current server.

It’s essentially a curl command with some formatting. First, it checks the domain name itself. If that passes, it checks for a random subdomain.

I had it include the curl time in the output because some domains were taking longer than others and I was interested to see where the slowdown was. Not that it does any troubleshooting, I just thought it was interesting to know.

Categories
Databases MySQL Productivity sqlite

Ill-Fated Attempt at Importing Quick Notes Data to Joplin

As you may have learned from the title, my attempt at importing data from Quick Notes to Joplin failed. I leave my notes here in case they save trouble for the next person.

Background

I was a Quick Notes user (which, to be clear, is mighty fine software, just not quite what I was looking for) for like two days. During that time I entered as many as ten notes. Obviously I’m not going to manually move such an enormous quantity of data, so I started for looking for ways to automate it.

“Not a problem!” I said to myself, “I’ll just dump the contents of one table into the other.” Well. Quick Notes uses MySQL and Joplin uses sqlite. That complicated things a bit.

“But,” you may be saying, “Wouldn’t you spend less time manually copying the notes over?” I refer you to xkcd.

Step 1 – Data from Quick Notes/MySQL to sqlite

Quick Notes uses MySQL whereas Joplin uses sqlite. The first order of business was to take the data dump from Quick Notes/MySQL and convert it into a sqlite file.

I tried a few things here. I ultimately wound up on an existing script to do the job for me, aptly named mysql2sqlite.

I saw instructions on RebaseData to make the change with curl. I found this resulted in a 4.6 MB file, but didn’t seem to have any tables. I leave the command here in case you want to pursue completing the conversion without downloading another script.

curl -F files[]=@file.sql 'file://[path to your mysql dump]?outputFormat=sqlite&errorResponse=zip' -o output.sqlite

Step 2 – the Joplin Database

Next you need a copy of the Joplin database. I found it in ~/.config/joplin-desktop/database.sqlite. If you go into the Joplin desktop app -> Options -> General, the location of the database is about the first line there. It’s easy to miss. I was not the only person who tried to locate it the hard way.

If you poke around in the databases you’ll find Joplin has a table called notes, which is fairly straightforward. Quick Notes has one called oc_quicknotes_notes.

Step 3 – Realize this Is More Complicated Than I Thought and Throw My Hands in the Air But Don’t Wave Them Like I Just Don’t Care

It was easy enough to insert from oc_quicknotes_notes into notes (attach then insert sub-select). Joplin has a table where it keeps track of what it’s synced and that’s what did in my efforts. I wasn’t able to cram the table full of guesswork well enough for Joplin not to choke on it.

Some other notes

  • The tags tables on both systems really only need an ID and a name. That in itself looks like an easy port. It looks like Joplin uses some kind of hash for the IDs, so the odds of an ID collision seem slim. That simplifies populating the pivot table.
  • Joplin is in Markdown but Quick Notes is HTML.
  • Attachments are probably a big job. In Quick Notes requires you to have uploaded a file to Next Cloud first, there’s no file upload dialog. Joplin has no such restriction.

Happy hacking!

Categories
The Madness

The Madness of 2020

Back in March, when San Diego County School District announced they were closing schools for three weeks, everyone I knew went, “Three weeks! What sort of madness is this?”

Prudent madness, as it turns out. That’s the tagline for this year: 2020, the year of prudent madness.

By the way, as of this writing, prudentmadness.com is available.

Categories
Productivity

Productivity Software Wishlist

I have a fever for productivity software. I haven’t found anything perfect (the nerve of other people not to think of exactly what I want!). This is wishlist of things ideal software would have. I wrote this both as a checklist for myself and as a guide of what to mention when I review other productivity software.

So, my nearly-true love is ❤️ Trello ❤️. Next best is Asana. This review is based on the things I found pleasing and displeasing about each.

Terminology

  • I use the terms tag and label interchangeably.
  • I use the terms card and task interchangeably. I realize that the Official Kanban Term is card, but I’m not bent on it.

Yes, Please

Basics

  • Kanban
  • The ability to copy and move boards, lists and tasks
  • The ability to do nearly anything in the app that you can do on the desktop and vice versa.
  • The ability to use other boards and cards as attachments. Pasting URLs will do in a pinch, but I’d like it to be able to grab the board’s name without me having to enter it.
  • Can see a list of all tasks assigned to me, regardless of board. Can sort these tasks by due date (among other things?). It remembers the sort.
  • Open source LOL yeah right
  • Native calendaring
  • Reminders on my phone. I’m not really pleased with how this is done in Trello, but I’m not sure what I would like. In Asana, reminders disappear after a day or so. I’d prefer they stick around until dismissed. This is useful if I’m waiting on some other party to complete a step.
  • The app should have basic functionality without an internet connection; it should sync automatically once the connection is restored.
  • Global IDs for each task so I can put ID-123 in a description or comment and it automatically links to that card, again regardless of board.
  • Attachments, including the ability to set an image as a cover on both cards and a list.
  • The ability to create templates from boards, lists or tasks
  • A library of ready-made templates.

Boards

  • Boards should be scoped to private by default.
  • The ability to create a template from a board
  • Alphabetical sorting on list of boards
  • Many pretty board backgrounds
  • Detailed notes on boards, lists or tasks. It would be nice if this integrates with Evernote or Google Keep or the like. Trello has board and task descriptions, but I’d like it to allow for more detailed notes. I think Notion has this.
  • The ability to see something as a board or a list with section headings.

Lists

  • Per-list notes
  • Per-list attachment

Tasks

  • Assign a task to multiple boards.
  • Multiple batches of checkboxes per task; the ability to promote a list item to a task
  • Per task detailed notes

Tags

  • Global
  • Color-coded
  • The ability to see a list of all tags, regardless of board – when I click on a tag, it should show me all the tasks across all the boards.

Other

  • No time limit on the free tier. Although I’m willing to pay for a service I love, a limited time trial period is inevitably too short to get a sense of whether or not a service works for me.
  • Analytics

What it Doesn’t Need But I’ll Tolerate

  • The ability to share things with other people
  • The ability to color-code tasks themselves

No, Thank You

  • Boards shared by default

Drop me a line if you have a lead on something like this – resume@kjcoop.com

Categories
Asana Productivity Trello

Productivity Review: Asana

I almost switched from 💌 Trello 💌 (not affiliated, just a fan) because it has global tags. Board-scoped tags is the one feature that’s keeping me from joining 💑 Trello 💑 in a union of holy matrimony. Ultimately, Asana was missing a lot of features that prevented me from fully embracing it.

What it Has Going for it

  • Covers basic kanban functionality nicely.
  • Aforementioned global and color-coded tags.
  • Can see a list of all tasks assigned to me, regardless of board. This is another thing 🌸 Trello 🌸 omits on the desktop.
  • It has the ability to see something as a board or a list with section headings. Scrolling down as opposed to over (and down) doesn’t really have a specific benefit, it’s just nice to see things displayed the way I think about them.
  • Native calendaring – 💖 Trello 💖 has plenty of plugins for this, but it doesn’t do it on its own
  • You can assign a task to multiple boards.
  • Occasional fun animations when you click done.
  • A nice library of templates
  • Its task notifications are pretty good.
  • Progress view, even at the free tier.
  • No time limit on the free tier. Although I’m willing to pay for a service I love (I pay for 💛 Trello 💛), a limited time trial period is inevitably too short to get a sense of whether or not a service works for me.

What it Lacks

  • The ability to duplicate a list to another board. While repeating oneself is bad practice (particularly in code), sometimes when you’re an end user of somebody else’s software, you just have to accept it.
  • Only a few pretty backgrounds to choose from. 💜 Trello 💜 must have some kind of intellectual property dibs on this.
  • Tag problems:
    • Can’t create a new tag in the app on Android.
    • No list of tags. If I want to see everything in the tag I used for things to be done during work hours, I have to go into a task with that tag, then click on the tag there.
    • Using them is buggy in the app. I once accidentally created two of the same tag (same name, same color). In my browser, I moved everything to one and deleted the other. The app still shows them both and fails silently when I guess incorrectly about which one is still there.
  • App won’t even show a complete list of boards without an internet connection.
  • Sorting options aren’t retained in the app. When you pull up a list, it’s not sorted in any way. It doesn’t want to guess what you want, that’s fine. I sort it by due date and I’m happy. I go away and come back. For a minute, it’s still sorted by due date, but then the list refreshes and returns to unsorted.
  • The ability to use other boards/tasks as attachments.

Not Lacking But Distasteful

  • Boards are default scoped to public within organization.
  • Although it’s theoretically nice that on the website you can change the board sort, it comes at the expense of having Asana alphabetize them.

Conclusion

I’m still contemplating getting a 😍 Trello 😍 tattoo; running out of ❤️ emojis.

Categories
Linux Pi-hole SSH The Horror!

Pi-hole

I recently installed Pi-hole on a brand spankin’ new Raspberry Pi to blacklist advertisements.

The install was very simple – all I had to do was follow their directions. My only frustration was that I forgot to enable SSH to start on boot. Evidently chkconfig has gone out of style, so I had to google The New Way. I suffered from ignorance for like 30 seconds. The horror!

I couldn’t find out how I change the DNS server in the router. I fear I shall have to contact my ISP. The horror!

Categories
Trello

Productivity Review: 🌈 Trello 🌈

I have mentioned my love for Trello. I’ve also been trying to work around the lack of global tags since about the time I began using it on Dec 31, 2018.

This shortcoming finally lead me on a trip around the world of kanban software. I plan to review some of the stops I made. First off, I’d like to write a love letter to Trello on what may be the eve of our separation.

Some things I love about Trello:

  • The primary functionality: kanban. I didn’t realize that’s what my life was missing until Trello came along. To-do lists just don’t cut it anymore.
  • The pretty background pictures. They call the Unsplash API to allow you to select a pretty background. It is surprisingly pleasant. I assume this hasn’t been copied due to some intellectual property reason. If/When Trello and I part ways, I’ll miss it.
  • The ability to use other boards and cards as attachments. It turns out to be surprisingly handy.
  • The ability to copy lists and move copies to other boards.
  • I have yet to find anything I can do on the website that I can’t do in the app.

The thing I don’t love:

  • It’s very siloed

Things I’d love to see

  • Global tags
  • A dashboard
  • It’s the nature of Trello’s beast to be closed source, but I’d sure love to find an alternative that’s so free rms would approve.

I’ve found a few open source web-based Kanban packages on Github, but none have all the features I’d need to be wooed away. I’m currently scoping out a few to potentially add missing functionality myself, but I’m still in the market.