Interactive dashboard for COVID-19 India

NOTE: The scale of the spread has meant that the individual data is no longer relevant or useful. This visualization has been removed to reduce any confusion. See the main dashboard for up to date information.

I stumbled across the Covid-19 India Ops telegram group and through that the covid19india.org dev group. The people there have setup a website for live tracking, a way to report new patients and even a cluster map. They also have a live updated API end-point that provides patient data in JSON. Not limited to the total number of cases and their location, the data has patient demographics and reporting dates.

I started working on a tool that others can use to make sense of the data. Since I don’t understand the domain and nuances of the data, I have made no attempt to interpret it.

You can check-out the live dashboard here — Interactive Covid-19 India Dashboard — please share it with someone who might find it useful.

The above dashboard was made using dc.js which brings crossfilter and d3 together to be able to filter the visualisation on the fly by interacting with the charts. While making this I had help from two Sanjaya’s — one Sanjay helped me figure out the GeoJSON file and the projects, and another Sanjaya pointed out some key UX improvements. There are still quite a few things to fix and I’ve noted them in Github issues. As always, pull requests are welcome on Github.

Sausages & Hamburgers

New Wink Docdash theme

Over the last few weeks I have been giving the WinkJS’s JSDoc theme some much needed attention. When we started using JSDoc for WinkJS packages we used the docdash theme. It was clean and simple, exactly what we needed at the time. After we update the main website though, the theme needed to look more on brand and so we forked it to add our colors and a navigation bar on top. We want to add tutorials for our packages soon and this means some more layout and design changes.

After initially adding the navigation bar, everything looked alright on desktop, but on mobile the two menus were fighting for attention—one would open a dropdown with the site-wide navigation, and the other would open a slide out with the API’s table of contents. The icons were quite close to each other and it was a mess.

hamburger and sausage menu diagram
Diagram of a few mobile menu options

So, when Siddharth showed me what sausage links were, I knew they would be perfect for this problem. I did some research and found some other documentation websites using them too. It is elegant and uses a simple horizontal scroll to show more items in the navigation. The trick is to be able to cut-off one of the menu items and show a fade on top of it so that its clear that there is something else there to see. Different screen widths mean that you can’t always control what is hidden so I decided to show a bigger fade on the left.

Demo of hamburger and sausage menus in action


Zuari on WordPress.org

After staying in review for nearly seven weeks, my WordPress theme finally got approved and deployed on the WordPress.org theme directory. Big thanks to @rabmalin for the review 🙏🏽

The Zuari theme on latest page of WordPress.org theme directory.
The Zuari theme as seen on WordPress.org

I wanted to make a theme that supported the new Gutenberg block styles, and gave a lot of customization options to the users. I have been working on it on and off since November last year. The idea was to give it an hour everyday, but travel and life did not let me be as consistent as I’d like. By March I realized that it might be better to work on a subset of features and do a version one release first. Now that this is done, I am excited about making improvements.


  • Use ITCSS to better architect the CSS. It’ll also help in getting the editor styles out.
  • Add microformats support for the IndieWeb.
  • Plugin support for the IndieWeb plugins.
  • Improve animations.
  • Bug fixes.
  • More customization options.
  • Accessibility improvements.

Hopefully by the end of the year I can actually recommend the theme to people who want to join the IndieWeb 🤞🏽 If you give it a spin and run into problems do raise an issue and I’ll try to help. Pull requests are welcome too!

Migrating to Dreamhost

My websites have been hosted on WebFaction since I can I remember, but with them recently getting acquired by GoDaddy, I started to look for alternatives. Looking for a cheap shared host to keep the three WordPress sites I maintain, I decided to check WordPress.org for recommendations. They suggested Bluehost, DreamHost and SiteGround. After a week of trying, Bluehost support couldn’t convert my domain account to a hosting account so I setup a new one on DreamHost.

The migration went smoothly but it took me a while to get used to the way DreamHost manages users, sites, and apps. Now that the DNS has (hopefully) fully propagated, the website is up and running on the new server, on https (for the first time 🥳), and using the new theme I am working on. Excited to be working on my site again!

Docker Compose for local WordPress development

Docker, specifically Docker compose, has served me well in keeping a sane development environment for my WordPress projects. MAMP was too much of a black box, and having a local PHP and SQL for both MediaWiki and WordPress was going to be more maintenance than I would have time for. With compose I have little to no maintenance burden, and my system is squeaky clean.

The following docker-compose.yml file starts two services — using the mysql, and the wordpress images:

version: '3.3'
     image: mysql:5.7
       - ./dbdata:/var/lib/mysql
     restart: always
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
       - db
     image: wordpress:latest
       - "8008:80"
       - ./wp-content:/var/www/html/wp-content
     restart: always
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress

The way this is setup is that both the services share a part of their file system with the host. The mysql one shares all the database data, making it easier for me to move test data around different instances and configurations of WordPress that I might be running. And, to be able to work on plugins and themes the wordpress service shares the wp-content directory. This way, I can easily clone and work on anything directly on my local system.

With this, all I need to do now to setup a new WordPress install is — create a new directory, copy this YAML file into it, and run docker-compose up. I change the wordpress service’s port mapping when I need to run multiple instances at once. That’s it!

Remember to Breathe

During a long chat with Nick, I discovered a solution to one of my long standing problems. I had a list of advice to myself on Google Keep. Not wanting to set it as my browser homepage I tried to make a habit of going to that page everyday. This obviously did not last very long, and soon, there was an item on my todo list that said, “Figure out what do to with advice to yourself”. This stupid, and funnily worded todo item remained on my list for almost a year.

Until of course Nick showed me his homepage. Yes, he had built a homepage for himself, long before the flood of “start page” browser plugins. Among the links to commonly visited pages, and the nine different search fields, what intrigued me most was the word “breathe” randomly written in the middle column. Refresh the page and the word changed to “simplify”. He had programmed it to pseudo-randomly choose from a list of words that he had selected. Jackpot!

breatho logo

Not being as old-school as he is, I decided to make a “start page” browser plugin. This was around the time of the bittersweet Mozilla announcement:

By the end of 2017, and with the release of Firefox 57, we’ll move to WebExtensions exclusively, and will stop loading any other extension types on desktop.

breathe screenshot

With support being added for chrome_url_overrides in Firefox 54, I started work on Breathe. A WebExtension that shows you a message from your pre-decided list every time you open a new tab.

You can download the extension from AMO right now, I am still working on getting it published on the Chrome Web Store (Update: Published!). If you don’t like waiting (for the Chrome version, or for updates) you can install it directly from the source.

MediaWiki API errors in FileAnnotations

I am working on FileAnnotations’ error handling. When I started writing the patch I wasn’t aware of all the cases we’ll have to tackle, Mark was nice enough to guide. Below is a list of errors that could occur and how to handle them.

AbuseFilter does not reject the mw.Api promise, but returns an object that has a warning.

  "edit": {
    "code": "abusefilter-disallowed",
    "message": {
      "key": "abusefilter-disallowed",
      "params": [
        "Test filter",
    "abusefilter": {
      "id": 2,
      "description": "Test filter",
      "actions": [
    "info": "Hit AbuseFilter: Test filter",
    "warning": "This action has been automatically identified as harmful, and therefore disallowed.\nIf you believe your action was constructive, please inform an administrator of what you were trying to do.\nA brief description of the abuse rule which your action matched is: Test filter",
    "result": "Failure"

Use the abusefilter-disallowed message here.

No edit permission
This one rejects the promise and the function has the error code as a string and an object literal that has details. It has the same object again as the third argument, I wonder what that is about.

  "error": {
    "code": "permissiondenied",
    "info": "The \"permissiondenied\" right is required to edit this page",
    "*": "See http://localhost:8080/w/api.php for API usage"

Should probably not show the add annotation interface if the user does not have permissions in the first place. In case we do, use the permissionserror message from core.

Session expired
Tested a few cases for this. If the user is logged out in the middle of adding an annotation they’d get a permissiondenied error. But if one logs out and logs into a different account in another tab, the new edit is correctly attributed to the newly logged in account.

Unreachable server
I found the api-error-http error message but haven’t been able to figure out how or when it triggers. Have filed a separate task for now.

This rejects the promise with error messages too. Could show api-error-blocked here, but it doesn’t show some of the useful information that is returned.

  "error": {
    "code": "blocked",
    "info": "You have been blocked from editing",
    "blockinfo": {
      "blockid": 1,
      "blockedby": "Admin",
      "blockedbyid": 1,
      "blockreason": "",
      "blockedtimestamp": "2016-09-27T20:25:49Z",
      "blockexpiry": "2016-09-27T22:25:49Z"
    "*": "See http://localhost:8080/w/api.php for API usage"

Protected page
Should protecting a File page automatically protect the Annotations page too? This is the same as some of the other errors, and core already has a protectedpage message.

  "error": {
    "code": "protectedpage",
    "info": "The \"editprotected\" right is required to edit this page",
    "*": "See http://localhost:8080/w/api.php for API usage"

Looking at all the api-error-* messages in core, these might show up too.

  • autoblock
  • badtoken
  • http (timeout error?)
  • timeout
  • unclassified

Here is the updated patch.


I posit that many people use EmacsAsOperatingSystem. What’s more, it’s about the most portable operating system ever. This would explain the fanaticism required to build mailers, newsreaders, web browsers, directory editors, CVS interfaces, etc etc etc into this one tool, so its proponents never have to leave the safety of their house.