sunrise-sunset: Update Streak Calculator if a Streak has just ended

This commit is contained in:
Thomas 2023-02-01 22:19:55 +00:00
parent 89df3538b4
commit daf73c797e
6 changed files with 109 additions and 90 deletions

View file

@ -0,0 +1,5 @@
export enum GuessType {
correct = 'correct',
incorrect = 'incorrect',
missing = 'missing',
}

View file

@ -8,7 +8,6 @@
export let currentStreakLength: number;
const todayAsString = formatDate(new Date(), "yyyy-MM-dd");
$: totalGuessCount = incorrectGuessDays.length + correctGuessDays.length;
const calculator = new SunriseSunsetStreakCalculator(todayAsString);
let hasTextBeenCopied = false;

View file

@ -0,0 +1,23 @@
import { parse } from 'date-fns';
import { GuessType } from './GuessType.js';
export class SunriseSunsetDayGuess {
readonly day: Date;
readonly emoji: string;
private static getEmojiForGuessType(guessType: GuessType): string {
switch (guessType) {
case GuessType.correct:
return '🎉';
case GuessType.incorrect:
return '💔';
case GuessType.missing:
return '🥷';
}
}
constructor(dayString: string, guessType: GuessType) {
this.day = parse(dayString, 'yyyy-MM-dd', new Date());
this.emoji = SunriseSunsetDayGuess.getEmojiForGuessType(guessType);
}
}

View file

@ -0,0 +1,53 @@
import { isSameDay, isBefore, addDays, format as formatDate } from 'date-fns';
import { SunriseSunsetDayGuess } from './SunriseSunsetDayGuess.js';
import { GuessType } from './GuessType.js';
export class SunriseSunsetDayGuessSet {
private readonly sortedGuesses: SunriseSunsetDayGuess[];
private readonly sortedGuessesWithMissingDays: SunriseSunsetDayGuess[];
constructor(guesses: SunriseSunsetDayGuess[]) {
this.sortedGuesses = guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
const missingGuesses = this.getGuessesForMissingDays();
this.sortedGuessesWithMissingDays = SunriseSunsetDayGuessSet.sortGuesses([...guesses, ...missingGuesses]);
}
private static sortGuesses(guesses: SunriseSunsetDayGuess[]): SunriseSunsetDayGuess[] {
return guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
}
private isGuessPresentForDay(date: Date): boolean {
return this.sortedGuesses.some((guess) => isSameDay(guess.day, date));
}
private getGuessesForMissingDays(): SunriseSunsetDayGuess[] {
const earliestDate: Date | undefined = this.sortedGuesses[this.sortedGuesses.length - 1]?.day;
if (!earliestDate) {
return [];
}
const latestDate = this.sortedGuesses[0].day;
let guessesForMissingDays: SunriseSunsetDayGuess[] = [];
let currentDay = earliestDate;
while (isBefore(currentDay, latestDate)) {
const isPresent = this.isGuessPresentForDay(currentDay);
if (!isPresent) {
guessesForMissingDays = [
...guessesForMissingDays,
new SunriseSunsetDayGuess(formatDate(currentDay, 'yyyy-MM-dd'), GuessType.missing),
];
}
currentDay = addDays(currentDay, 1);
}
return guessesForMissingDays;
}
getLastDays(maxDays = 10): SunriseSunsetDayGuess[] {
return this.sortedGuessesWithMissingDays.slice(0, maxDays);
}
}

View file

@ -88,10 +88,10 @@ describe('SunriseSunsetStreakCalculator', () => {
});
describe(`Streak Visualisation`, () => {
let calculator: SunriseSunsetStreakCalculator;
let streakCalculator: SunriseSunsetStreakCalculator;
beforeAll(() => {
calculator = new SunriseSunsetStreakCalculator('2023-01-29');
streakCalculator = new SunriseSunsetStreakCalculator('2023-01-29');
});
it(`Sound visualise an empty streak`, () => {
@ -100,7 +100,7 @@ describe('SunriseSunsetStreakCalculator', () => {
const incorrectDays = [];
// WHEN
const emojiVisualisation = calculator.getEmojiForHistory(correctDays, incorrectDays);
const emojiVisualisation = streakCalculator.getEmojiForHistory(correctDays, incorrectDays);
// THEN
expect(emojiVisualisation).toBe('');
@ -112,19 +112,32 @@ describe('SunriseSunsetStreakCalculator', () => {
const incorrectDays = ['2023-01-19'];
// WHEN
const emojiVisualisation = calculator.getEmojiForHistory(correctDays, incorrectDays);
const emojiVisualisation = streakCalculator.getEmojiForHistory(correctDays, incorrectDays);
// THEN
expect(emojiVisualisation).toBe('🎉💔');
});
it(`should recognise a streak which has just ended`, () => {
// GIVEN
const today = new Date('2023-02-01');
const correctDays = ['2023-01-31', '2023-01-30'];
const calculator = new SunriseSunsetStreakCalculator('2023-02-01');
// WHEN
const statement = calculator.getShareableStatement(correctDays, [], today);
// THEN
expect(statement).toContain(`Current Streak: 0`);
});
it(`should handle a missing day`, () => {
// GIVEN
const correctDays = ['2023-01-20', '2023-01-21'];
const incorrect = ['2023-01-18', '2023-01-22'];
// WHEN
const emojiVisualisation = calculator.getEmojiForHistory(correctDays, incorrect);
const emojiVisualisation = streakCalculator.getEmojiForHistory(correctDays, incorrect);
// THEN
expect(emojiVisualisation).toBe('💔🎉🎉🥷💔');
@ -137,7 +150,7 @@ describe('SunriseSunsetStreakCalculator', () => {
const today = new Date('2023-01-20T21:52Z');
// WHEN
const shareableStatement = calculator.getShareableStatement(correctDays, incorrectDays, today);
const shareableStatement = streakCalculator.getShareableStatement(correctDays, incorrectDays, today);
// THEN
const expected = `Sunrise, Sunset?\n2023-01-20\n\nCurrent Streak: 0\nLongest Streak: 0`;
@ -150,6 +163,8 @@ describe('SunriseSunsetStreakCalculator', () => {
const incorrectDays = ['2023-01-28'];
const today = new Date('2023-01-31T21:52Z');
const calculator = new SunriseSunsetStreakCalculator('2023-01-31');
// WHEN
const shareableStatement = calculator.getShareableStatement(correctDays, incorrectDays, today);

View file

@ -1,88 +1,14 @@
import { parse, format as formatDate, isAfter, isBefore, isSameDay, addDays, differenceInCalendarDays } from 'date-fns';
enum GuessType {
correct = 'correct',
incorrect = 'incorrect',
missing = 'missing',
}
class SunriseSunsetDayGuess {
readonly day: Date;
readonly emoji: string;
private static getEmojiForGuessType(guessType: GuessType): string {
switch (guessType) {
case GuessType.correct:
return '🎉';
case GuessType.incorrect:
return '💔';
case GuessType.missing:
return '🥷';
}
}
constructor(dayString: string, private readonly guessType: GuessType) {
this.day = parse(dayString, 'yyyy-MM-dd', new Date());
this.emoji = SunriseSunsetDayGuess.getEmojiForGuessType(guessType);
}
}
class SunriseSunsetDayGuessSet {
private readonly sortedGuesses: SunriseSunsetDayGuess[];
private readonly sortedGuessesWithMissingDays: SunriseSunsetDayGuess[];
constructor(guesses: SunriseSunsetDayGuess[]) {
this.sortedGuesses = guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
const missingGuesses = this.getGuessesForMissingDays();
this.sortedGuessesWithMissingDays = SunriseSunsetDayGuessSet.sortGuesses([...guesses, ...missingGuesses]);
}
private static sortGuesses(guesses: SunriseSunsetDayGuess[]): SunriseSunsetDayGuess[] {
return guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
}
private isGuessPresentForDay(date: Date): boolean {
return this.sortedGuesses.some((guess) => isSameDay(guess.day, date));
}
private getGuessesForMissingDays(): SunriseSunsetDayGuess[] {
const earliestDate: Date | undefined = this.sortedGuesses[this.sortedGuesses.length - 1]?.day;
if (!earliestDate) {
return [];
}
const latestDate = this.sortedGuesses[0].day;
let guessesForMissingDays: SunriseSunsetDayGuess[] = [];
let currentDay = earliestDate;
while (isBefore(currentDay, latestDate)) {
const isPresent = this.isGuessPresentForDay(currentDay);
if (!isPresent) {
guessesForMissingDays = [
...guessesForMissingDays,
new SunriseSunsetDayGuess(formatDate(currentDay, 'yyyy-MM-dd'), GuessType.missing),
];
}
currentDay = addDays(currentDay, 1);
}
return guessesForMissingDays;
}
getLastDays(maxDays = 10): SunriseSunsetDayGuess[] {
return this.sortedGuessesWithMissingDays.slice(0, maxDays);
}
}
import { parse, format as formatDate, differenceInCalendarDays } from 'date-fns';
import { SunriseSunsetDayGuess } from './SunriseSunsetDayGuess.js';
import { SunriseSunsetDayGuessSet } from './SunriseSunsetGuessSet.js';
import { GuessType } from './GuessType.js';
class SunriseSunsetStreak {
readonly longestStreak: number;
private readonly allStreaks: number[];
readonly mostRecentStreak: number;
constructor(correctDays: Date[]) {
constructor(correctDays: Date[], today = new Date()) {
if (correctDays.length === 0) {
this.longestStreak = 0;
this.mostRecentStreak = 0;
@ -155,20 +81,18 @@ export class SunriseSunsetStreakCalculator {
const emoji = this.getEmojiForHistory(correctDays, incorrectDays);
const todayFormatted = formatDate(today, 'yyyy-MM-dd');
const streak = new SunriseSunsetStreak(this.daysAsDates(correctDays));
const currentStreak = this.getStreakLength(correctDays);
return [
`Sunrise, Sunset?`,
todayFormatted,
emoji,
`Current Streak: ${streak.mostRecentStreak}`,
`Current Streak: ${currentStreak}`,
`Longest Streak: ${streak.longestStreak}`,
].join(joiningString);
}
getStreakLength(correctDays: string[]): number {
console.log(`getStreakLength`);
console.log(correctDays);
if (correctDays.length === 0) {
console.log(`No correct days, returning 0.`);
return 0;