Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync old season before beginning new season #55

Merged
merged 2 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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