Skip to content

Commit

Permalink
Sync old season before beginning new season (#55)
Browse files Browse the repository at this point in the history
* add `sync_test.ts` tests

* call `await this.listPlayers()` once
  • Loading branch information
EthanThatOneKid authored Dec 7, 2023
1 parent c506110 commit bf61bf1
Show file tree
Hide file tree
Showing 9 changed files with 347 additions and 136 deletions.
216 changes: 108 additions & 108 deletions deno.lock

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
export {
assertEquals,
assertRejects,
} from "https://deno.land/[email protected]/assert/mod.ts";

export { load } from "https://deno.land/[email protected]/dotenv/mod.ts";
export * from "https://deno.land/[email protected]/datetime/constants.ts";
} from "https://deno.land/[email protected]/assert/mod.ts";
export { load } from "https://deno.land/[email protected]/dotenv/mod.ts";
export * from "https://deno.land/[email protected]/datetime/constants.ts";
export { ulid } from "https://deno.land/x/[email protected]/mod.ts";
export type {
APIApplicationCommandInteractionDataOption,
Expand All @@ -17,12 +16,12 @@ export type {
APIUser,
RESTPostAPIApplicationCommandsJSONBody,
RESTPostAPIWebhookWithTokenJSONBody,
} from "https://deno.land/x/[email protected].63/v10.ts";
} from "https://deno.land/x/[email protected].65/v10.ts";
export {
ApplicationCommandOptionType,
InteractionResponseType,
InteractionType,
MessageFlags,
Utils,
} from "https://deno.land/x/[email protected].63/v10.ts";
} from "https://deno.land/x/[email protected].65/v10.ts";
export { default as nacl } from "https://cdn.skypack.dev/[email protected]";
21 changes: 15 additions & 6 deletions lib/api/dailies.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type APIEmbed } from "lc-dailies/deps.ts";
import { type APIEmbed, WEEK } from "lc-dailies/deps.ts";
import * as api from "./mod.ts";
import * as discord from "lc-dailies/lib/discord/mod.ts";
import * as router from "lc-dailies/lib/router/mod.ts";
Expand Down Expand Up @@ -90,11 +90,20 @@ async function executeDailyWebhook(

// If the season is ongoing, then sync it.
const referenceDate = new Date();
const isLatestSeason = storedSeason && leaderboard.checkDateInWeek(
new Date(storedSeason.start_date).getTime(),
referenceDate.getTime(),
);
const syncedSeason = isLatestSeason

let isLatestSeason = false;
if (storedSeason) {
const seasonStartDate = new Date(storedSeason.start_date).getTime();
const seasonEndDate = seasonStartDate + WEEK;
isLatestSeason = leaderboard.checkDateBetween(
seasonStartDate,
seasonEndDate,
referenceDate.getTime(),
);
}

// Sync the season if it is ongoing and not synced.
const syncedSeason = isLatestSeason && storedSeason
? await leaderboardClient
.sync(storedSeason.id)
.then((response) => response.season)
Expand Down
4 changes: 2 additions & 2 deletions lib/lc/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export class LCClient implements LCClientInterface {
public async getDailyQuestion(): Promise<LCQuestion> {
const date = new Date();
const [question] = await this.listDailyQuestions(
1,
date.getFullYear(),
date.getMonth() + 1,
1,
);
if (!question) {
throw new Error("No daily question found");
Expand All @@ -43,9 +43,9 @@ export class LCClient implements LCClientInterface {
* listDailyQuestions gets the last `amount` of daily questions from Leetcode since `asOfYear` and `asOfMonth`.
*/
public async listDailyQuestions(
limit: number,
asOfYear: number,
asOfMonth: number,
limit = 10,
): Promise<LCQuestion[]> {
const dailies: LCQuestion[] = [];
let currentYear = asOfYear;
Expand Down
2 changes: 1 addition & 1 deletion lib/lc/client_interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ export interface LCClientInterface {
* listDailyQuestions gets the last `amount` of daily questions from Leetcode since `asOfYear` and `asOfMonth`.
*/
listDailyQuestions(
limit: number,
asOfYear: number,
asOfMonth: number,
limit?: number,
): Promise<LCQuestion[]>;

/**
Expand Down
19 changes: 18 additions & 1 deletion lib/leaderboard/denokv/denokv_leaderboard_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export class DenoKvLeaderboardClient implements LeaderboardClient {
let seasonStartDate: Date;
let isLatestSeason: boolean;
let seasonResult: Deno.KvEntryMaybe<api.Season> | null;
const players = await this.listPlayers();
if (seasonID) {
seasonResult = await this.kv.get<api.Season>([
LeaderboardKvPrefix.SEASONS,
Expand All @@ -156,6 +157,23 @@ export class DenoKvLeaderboardClient implements LeaderboardClient {
const isPresentAndLatest = seasonResult?.value &&
new Date(seasonResult.value.start_date).getTime() ===
startOfWeekDate.getTime();

if (!isPresentAndLatest && seasonResult?.value) {
// Sync old season.
const oldSeason = await sync({
lcClient: this.lc,
players,
season: seasonResult.value,
});
oldSeason.synced_at = referenceDate.toUTCString();

// Store the synced old season.
await this.kv.set(
[LeaderboardKvPrefix.SEASONS, oldSeason.id],
oldSeason,
);
}

season = isPresentAndLatest
? seasonResult?.value!
: makeEmptySeason(startOfWeekDate);
Expand All @@ -164,7 +182,6 @@ export class DenoKvLeaderboardClient implements LeaderboardClient {
}

// Sync the season.
const players = await this.listPlayers();
season = await sync({ lcClient: this.lc, players, season });
season.synced_at = referenceDate.toUTCString();

Expand Down
27 changes: 21 additions & 6 deletions lib/leaderboard/denokv/denokv_leaderboard_client_test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertEquals, assertRejects, DAY } from "lc-dailies/deps.ts";
import { assertEquals, assertRejects, DAY, WEEK } from "lc-dailies/deps.ts";
import * as fake_lc from "lc-dailies/lib/lc/fake_client.ts";
import type { Season } from "lc-dailies/lib/api/mod.ts";
import { DenoKvLeaderboardClient } from "./denokv_leaderboard_client.ts";
Expand Down Expand Up @@ -60,32 +60,47 @@ Deno.test("DenoKvLeaderboardClient", async (t) => {
);
const syncResponse = await client
.sync(undefined, twoDaysAfterFakeSeasonStartDate);
assertSeasonsEqual(syncResponse.season, FAKE_SEASON);
assertSeasonsEquals(syncResponse.season, FAKE_SEASON);
});

let seasonID: string | undefined;
await t.step("getLatestSeason", async () => {
const season = await client.getLatestSeason();
seasonID = season?.id;
assertSeasonsEqual(season, FAKE_SEASON);
assertSeasonsEquals(season, FAKE_SEASON);
});

await t.step("sync again", async () => {
const weekAfterFakeSeasonStartDate = new Date(
FAKE_SEASON_START_DATE.getTime() + WEEK,
);
const syncResponse = await client
.sync(undefined, weekAfterFakeSeasonStartDate);
assertEquals(
syncResponse.season.start_date,
"Sun, 06 Aug 2023 00:00:00 GMT",
);
assertEquals(syncResponse.season.submissions, {});
});

await t.step("listSeasons", async () => {
const seasons = await client.listSeasons();
assertEquals(seasons.length, 2);

const season = seasons[0];
assertSeasonsEqual(season, FAKE_SEASON);
assertSeasonsEquals(season, FAKE_SEASON);
});

await t.step("getSeason", async () => {
const season = await client.getSeason(seasonID!);
assertSeasonsEqual(season, FAKE_SEASON);
assertSeasonsEquals(season, FAKE_SEASON);
});

// Dispose of the resource.
kv.close();
});

function assertSeasonsEqual(
function assertSeasonsEquals(
actualSeason: Season | null,
expectedSeason: Season,
): void {
Expand Down
18 changes: 12 additions & 6 deletions lib/leaderboard/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export async function sync(options: SyncOptions): Promise<api.Season> {
const seasonStartDate = new Date(options.season.start_date);
const seasonEndDate = new Date(seasonStartDate.getTime() + WEEK);
const recentDailyQuestions = await options.lcClient.listDailyQuestions(
options.questionsFetchAmount ?? 10,
seasonEndDate.getUTCFullYear(),
seasonEndDate.getUTCMonth() + 1,
options.questionsFetchAmount,
);

// Fetch the submissions of the players.
Expand All @@ -69,8 +69,9 @@ export async function sync(options: SyncOptions): Promise<api.Season> {
}

// Skip if the submission is not in the season.
const isSubmissionInSeason = checkDateInWeek(
const isSubmissionInSeason = checkDateBetween(
seasonStartDate.getTime(),
seasonEndDate.getTime(),
submissionDate.getTime(),
);
if (!isSubmissionInSeason) {
Expand All @@ -90,8 +91,9 @@ export async function sync(options: SyncOptions): Promise<api.Season> {

// Skip if the question is not in the season.
const questionDate = new Date(recentDailyQuestion.date);
const isQuestionInSeason = checkDateInWeek(
const isQuestionInSeason = checkDateBetween(
seasonStartDate.getTime(),
seasonEndDate.getTime(),
questionDate.getTime(),
);
if (!isQuestionInSeason) {
Expand Down Expand Up @@ -135,8 +137,12 @@ export function fromLCTimestamp(timestamp: string): Date {
}

/**
* checkDateInWeek checks if a date is in a week.
* checkDateBetween checks if a date is in a given duration.
*/
export function checkDateInWeek(startOfWeek: number, date: number): boolean {
return date >= startOfWeek && date < startOfWeek + WEEK;
export function checkDateBetween(
start: number,
end: number,
date: number,
): boolean {
return date >= start && date < end;
}
Loading

0 comments on commit bf61bf1

Please sign in to comment.