mirror of
https://github.com/prdlk/leetcode.git
synced 2026-09-17 07:26:27 +00:00
feat(api): extract difficulty and set from issue labels in catalog reconciliation
This commit is contained in:
+34
-4
@@ -4,11 +4,19 @@
|
||||
* rows from scratch on every run — re-runs are no-ops. SRS-owned columns
|
||||
* (stage, next_review) are NEVER overwritten; defer_until is set once, on
|
||||
* first sight of a deferred problem. The Worker never invents catalog rows.
|
||||
*
|
||||
* Difficulty and set come from the `diff:*` / `set:*` LABELS, never from the
|
||||
* issue title — the title carries only the LC number and the problem's name,
|
||||
* so relabelling a problem is enough to move it and titles stay readable.
|
||||
* The `problem` label is what marks a sub-issue as curriculum.
|
||||
*/
|
||||
import { addDays } from "./srs.ts";
|
||||
import type { GitHub } from "./github.ts";
|
||||
|
||||
const TITLE_RE = /^LC (\d+) · (.+) · (Easy|Medium|Hard) · (core|optional|deferred)$/;
|
||||
const TITLE_RE = /^LC (\d+) · (.+)$/;
|
||||
|
||||
const DIFFICULTIES = ["easy", "medium", "hard"] as const;
|
||||
const SETS = ["core", "optional", "deferred"] as const;
|
||||
|
||||
/** Deferred Hards enter the queue from this date, two per day. */
|
||||
const DEFER_FROM = "2026-09-28";
|
||||
@@ -18,6 +26,18 @@ export interface ReconcileReport {
|
||||
problems: number;
|
||||
}
|
||||
|
||||
/** Label names on one issue payload, narrowed with guards rather than cast. */
|
||||
function labelNames(value: object): string[] {
|
||||
if (!("labels" in value) || !Array.isArray(value.labels)) return [];
|
||||
const names: string[] = [];
|
||||
for (const label of value.labels) {
|
||||
if (label && typeof label === "object" && "name" in label && typeof label.name === "string") {
|
||||
names.push(label.name);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
export async function reconcileCatalog(db: D1Database, gh: GitHub): Promise<ReconcileReport> {
|
||||
interface TopicIssue {
|
||||
number: number;
|
||||
@@ -62,10 +82,20 @@ export async function reconcileCatalog(db: D1Database, gh: GitHub): Promise<Reco
|
||||
if (!raw || typeof raw !== "object") continue;
|
||||
if (!("number" in raw) || typeof raw.number !== "number") continue;
|
||||
if (!("title" in raw) || typeof raw.title !== "string") continue;
|
||||
const labels = labelNames(raw);
|
||||
if (!labels.includes("problem")) continue; // non-curriculum sub-issue
|
||||
const m = raw.title.match(TITLE_RE);
|
||||
if (!m) continue; // non-curriculum sub-issue
|
||||
if (!m) {
|
||||
console.warn(`catalog: #${raw.number} is a problem but not titled "LC <n> · <name>"`);
|
||||
continue;
|
||||
}
|
||||
const difficulty = DIFFICULTIES.find((d) => labels.includes(`diff:${d}`));
|
||||
const set = SETS.find((s) => labels.includes(`set:${s}`));
|
||||
if (!difficulty || !set) {
|
||||
console.warn(`catalog: #${raw.number} is missing a diff:/set: label — skipped`);
|
||||
continue;
|
||||
}
|
||||
const lc = Number(m[1]);
|
||||
const set = m[4]!;
|
||||
// Two deferred Hards per day from DEFER_FROM, in catalog walk order —
|
||||
// applied only when the row is first created (SRS owns it afterwards).
|
||||
const defer = set === "deferred" ? addDays(DEFER_FROM, Math.floor(deferredSeen / 2)) : null;
|
||||
@@ -78,7 +108,7 @@ export async function reconcileCatalog(db: D1Database, gh: GitHub): Promise<Reco
|
||||
ON CONFLICT(lc_number) DO UPDATE SET
|
||||
issue = ?2, topic_issue = ?3, title = ?4, difficulty = ?5, set_label = ?6`,
|
||||
)
|
||||
.bind(lc, raw.number, t.number, m[2]!, m[3]!.toLowerCase(), set, defer),
|
||||
.bind(lc, raw.number, t.number, m[2]!, difficulty, set, defer),
|
||||
);
|
||||
problems++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user