[Userscript] Wanikani Heatmap

What is Wanikani Heatmap

This script adds a chart that shows which days you have reviewed and indicates with color how many reviews you did. Does the same with lessons. Along with this it also shows you some interesting stats such as how many days you have reviewed, how many reviews you do per day on average, and what your current streak is.

It utilises and is dependent on the Wanikani Open Framework to fetch the review data, so make sure it is installed.

What is WKOF

WKOF - Wanikani Open Framework - is a script by rfindley that makes it easier and more resource efficient to develop against the WK API. You need to install it and make it run before this script, for this one to work.

More information in this thread
Installing Wanikani Open Framework

Other Info

  • The script will not show any review data since before 4 August 2017, because Wanikani did not log review data before that, but all lessons are included in your lesson heatmap.

  • You can also choose exactly which colors you want to use.

  • Works fine with dark themes.


Screenshots

Reviews

Lessons

For more heatmaps have a look at this thread


Settings

There are quite a few settings available for this script.

  • Start date: A manual start date in case you fell off the wagon years ago and want the script to ignore anything before a certain date.

  • First day of the week: Can be set to either Monday or Sunday with the default being determined by your timezone. It changes how the heatmap is laid out.

  • New day starts at: A setting to offset the start of a new day. Useful if you stay up late and want those hours after midnight to count to the previous day.

  • Reverse year order: Puts the most recent year on the bottom instead of on top.

  • Display intervals: Displays your chosen intervals right under the heatmap.

  • Segment year: Divides the years into months.

  • Day of week labels: Displays the days of the week as labels to the left of each row in the heatmaps.

  • Show level-up indicators: Puts a border around dates when you leveled up.

  • Color for level-up indicators: The color of the border for level-up indicators.

  • Show current day indicator: Puts a border around the current day.

  • Color for current day indicator: The color of the border of the current day indicator.


The following is the same for reviews and lessons.

  • Use these colors: Use the colors you have chosen. If unticked the default colors will be used.

  • Start color: The starting color of the gradient for the heatmap. This is the color for the first interval.

  • Color 2-4: Middle colors for the middle intervals.

  • End color: The end color of the gradient for the heatmap. This is also the color for the open-ended fifth interval.

  • Generate button: This button sets the middle colors based on your start and end colors, to create an even gradient.

  • Auto range: Automatically chooses the intervals based on how many reviews or lessons you have done in the past.

  • End of interval 1: Days with a number of items between 0 and this number will have the related color. Not including 0.

  • End of interval 2-4: Days with a number of items between the previous number and this will have the related color.

  • Interval 5: Not a setting. Days with more items than what is entered in End of interval 4 will have the fifth color.

  • Reload: Deletes cached data so that it can be refetched again. Mind that reloading review data can take a few minutes if you have a lot of reviews under your belt.

Forecast settings only apply to reviews

  • Show forecast: Show or hide the foercast part of the review heatmap.

  • Forecast color settings: Works just like the other color settings, but this applies only for the forecast part of the review heatmaps.

Special streak setting for lessons

  • Count Zero Lesson Days: Count days on which you did no lesson but also had no lessons to do, towards the streak.

Available at

the local ice cream stand


Planned features for 2.1.0:

  • Click and drag between two dates to show stats in interval between them
  • Time spent reviewing/doing lessons
  • Number of review/lesson sessions
  • Burn count in summaries
  • Better tooltips in summaries
  • More information for the items in the summaries
  • Add color labels back, lol
  • Indicator for which of the items were burned
  • Cache review data! (found a way to do it in localStorage up to 400k reviews)

Known bugs:

  • Stops working in April 2019 in a specific timezone due to an unresolved bug in the heatmap library.
88 Likes

Nice script. You should add a note that WKOF is a prerequisite (and what it is/link) and is not bundled in the heatmap script, and if possible add a notification to the heatmap script that the WKOF is missing (dialog or whatever).

It will be easier for first-time users.

4 Likes

Maybe follow WK colors (from apprentice, guru, master…)

8 Likes

I have been tricked (ಥ_ʖಥ)

7 Likes

I thought I did
You didn’t get an alert?

// Make sure WKOF is installed
		if (!wkof) {
				var response = confirm('WaniKani Heatmap requires WaniKani Open Framework.\n Click "OK" to be forwarded to installation instructions.');
				if (response) window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
				return;
		}

I did try that, but it got rather messy. I think having a spectrum is more intuitive. Thanks for the suggestion though. I don’t really like the current colors.

6 Likes

Nope, no window.

I am not exactly sure how Tampermonkey works in Firefox, but the script itself is executed in the strict mode. In strict mode, they did some changes with the strict mode and undeclared variables… not really sure what (I am not a JS developer), the code itself is run in the eval() (or apply() or something).

Anyway, when I debug the code, once I reach the if (!wkof), the ReferenceError is thrown, unless I enable the WKOF in the Tapermonkey dashboard.

I guess the reason is same as why this (fix quotes, not sure why are they changed in the post):
(function() {“use strict”; eval(“if (!wk) { console.log(1);} else {console.log(2);}”); }())

throw ReferenceError: wk is not defined when I run it in the console.

Huh. I didn’t know about that. @rfindley do you know if there’s a good reason why Firefox would throw a ReferenceError? Should we maybe use a try-catch instead of if (!wkof)?

It’s wkof, not wk. Does it work with wkof?

Thanks so much for this! It’s really interesting to see.

3 Likes

Could change it to:

if (!window.wkof) ...

Or don’t turn on strict in your script (though I don’t know whether there’s a setting to make it default to strict, in which case some people may still have problems.).

2 Likes

I don’t have 'use strict' in the script, so I would assume it’s something on their end. I’ll use just put var wkof = window.wkof in my scripts from now on

1 Like

Awesome!! Looks very nice, thank you!
I love my review heatmap in Anki. I was not sooo excited about getting it for WaniKani since I could imagine what it would look like anyway (who doesn’t painfully remember when you had those few weeks where you didn’t review) but now that I see it, I love it :slight_smile:

In case you are interested in extending this further, here’s a screenshot of the info that the Anki plugin shows below the heatmap. This is also super motivating, I look at it even more than at the actual heatmap:

8 Likes

Sounds like good info for an update at some point ukKr5

6 Likes

I couldn’t wait :smiley:

Added no styling, just quickly threw something together. In case you want to reuse it. It is called in the same way as create_heatmap:

    function create_streakinfo(data) {
        var streakinfoSection = document.createElement('section');
        $(streakinfoSection).attr("class", "streakinfo");

        var daysWithReviews = 0;
        var daysWithoutReviews = 0;
        var longestStreak = 0;
        var totalReviews = 0;

        //get date without time (00:00) in order to match date in data
        let firstDate = new Date(new Date(data.first_date).getFullYear(), new Date(data.first_date).getMonth(), new Date(data.first_date).getDate());
        let lastDate = new Date(new Date(data.last_date).getFullYear(), new Date(data.last_date).getMonth(), new Date(data.last_date).getDate());

        var currentDate = firstDate;
        var currentStreak = 0;
        var previousDayHadReviews = false;

        while (currentDate <= lastDate) {
            var dateInSeconds = currentDate.getTime() / 1000;
            var count = data.counts[dateInSeconds];
            if (count) {
                if (previousDayHadReviews) {
                    currentStreak++;
                }
                totalReviews += count;
                daysWithReviews++;
                previousDayHadReviews = true;
            } else {
                if (currentStreak > longestStreak) {
                    longestStreak = currentStreak;
                }
                currentStreak = 0;
                daysWithoutReviews++;
                previousDayHadReviews = false;
            }
            currentDate.setDate(currentDate.getDate() + 1);
        }
        if (currentStreak > longestStreak) longestStreak = currentStreak;
        let averageReviews = Math.round(totalReviews / (daysWithReviews + daysWithoutReviews));
        let percentageWithReviews = Math.round(daysWithReviews / ((daysWithReviews + daysWithoutReviews) / 100));

        streakinfoSection.append("Average daily reviews: " + averageReviews + " Studied on " + percentageWithReviews + "% of days (" + daysWithReviews + " out of " + (daysWithReviews + daysWithoutReviews) + ") Longest streak: " + longestStreak + " Current streak: " + currentStreak );
        $('.progression').after(streakinfoSection);
    }

(I also didn’t double check super duper carefully so the calculation might be a bit off; it “felt right” and I decided I was done :wink: )

5 Likes

It looks super cool! Small idea, it might be fun to see when you had WaniKani on vacation mode. Is it possible to add that?

4 Likes

I pretty much never use strict mode, and I’d still expect it to throw an error if it doesn’t have “window.” in front and the variable hasn’t been declared or assigned yet. That’s just normal JavaScript. Maybe greasemonkey/tampermonkey handles that differently somehow?

1 Like

I like it! And the colors seem fine to me.

Minor thing: it time travels.
49%20PM

3 Likes

Seems like you wouldn’t want that to throw an error if the state of being undefined was one of the conditions you were trying to test. How would you get a false? or true for if-not, ykwim

2 Likes

Nice script Kumi! For colors, maybe yellow to dark red? It’s is a heat map after all.

This is really one of those things that @viet and @oldbonsai should implement as a proper feature though…

3 Likes

Hey, you made it! Thanks Kumi! You’re awesome.

1 Like

:ok_hand:

I definitely started on July 4th, is there no data prior to August 4th?

2 Likes