import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
|
|
import { IKanji, WaniKani } from '@/wanikani';
|
|
import yaml from 'js-yaml';
|
|
|
|
interface ILevelMap {
|
|
[category: string]: {
|
|
[levelString: string]: string[];
|
|
};
|
|
}
|
|
|
|
async function makeWaniKaniKanjiLevels(
|
|
opts: { cache?: boolean; force?: boolean } = {},
|
|
) {
|
|
const FILENAME = 'cache/wanikani-kanji.yaml';
|
|
if (opts.cache && existsSync(FILENAME)) {
|
|
return yaml.load(readFileSync(FILENAME, 'utf-8')) as ILevelMap;
|
|
}
|
|
|
|
const wkKanji = await new WaniKani()
|
|
.subjects({ force: !!opts.force })
|
|
.then((vs) => vs.filter((v) => v.object === 'kanji') as IKanji[]);
|
|
|
|
const levelMap: ILevelMap = {};
|
|
|
|
for (const k of wkKanji) {
|
|
const { level, characters } = k.data;
|
|
const label = WaniKani.getLevelLabel(level);
|
|
const category = `${label.range}: ${
|
|
label.ja
|
|
} ${label.en.toLocaleUpperCase()}`;
|
|
|
|
const catMap = levelMap[category] || {};
|
|
const levelString = level.toString().padStart(2, '0');
|
|
|
|
const ks = catMap[levelString] || [];
|
|
ks.push(characters);
|
|
catMap[levelString] = ks;
|
|
|
|
levelMap[category] = catMap;
|
|
}
|
|
|
|
writeFileSync(
|
|
FILENAME,
|
|
yaml.dump(levelMap, {
|
|
sortKeys: true,
|
|
flowLevel: 2,
|
|
}),
|
|
);
|
|
|
|
return levelMap;
|
|
}
|
|
|
|
export async function makeKanjiLevels(opts: { useBeyond: boolean }) {
|
|
const wk = await makeWaniKaniKanjiLevels({ cache: true });
|
|
const beyond = opts.useBeyond
|
|
? (yaml.load(readFileSync('assets/beyond.yaml', 'utf-8')) as ILevelMap)
|
|
: {};
|
|
|
|
const kanjiToLevel = new Map<
|
|
string,
|
|
{
|
|
level: number;
|
|
deckName: string;
|
|
}
|
|
>();
|
|
|
|
const setKanjiToLevel = (lvMap: ILevelMap) => {
|
|
Object.entries(lvMap).map(([cat, map]) => {
|
|
Object.entries(map).map(([levelString, ks]) => {
|
|
const level = Number(levelString);
|
|
ks.map((k) => {
|
|
let prev = kanjiToLevel.get(k);
|
|
if (!prev || prev.level > level) {
|
|
prev = {
|
|
level,
|
|
deckName: cat + '::' + levelString,
|
|
};
|
|
kanjiToLevel.set(k, prev);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
};
|
|
setKanjiToLevel(wk);
|
|
setKanjiToLevel(beyond);
|
|
|
|
return kanjiToLevel;
|
|
}
|
|
|
|
export async function repairBeyond() {
|
|
const beyond = yaml.load(
|
|
readFileSync('cache/beyond.yaml', 'utf-8'),
|
|
) as Record<string, string>;
|
|
|
|
const levelMap: ILevelMap = {};
|
|
|
|
for (const [levelString, vs] of Object.entries(beyond)) {
|
|
const level = Number(levelString);
|
|
|
|
let category: string;
|
|
if (level < 71) {
|
|
category = '61-70: 無限 INFINITY';
|
|
} else if (level < 76) {
|
|
category = '71-75: 極端 BEYOND';
|
|
} else {
|
|
category = '76+: 熟達 MASTERY';
|
|
}
|
|
|
|
const catMap = levelMap[category] || {};
|
|
|
|
const ks = catMap[levelString] || [];
|
|
ks.push(...vs);
|
|
catMap[levelString] = ks;
|
|
|
|
levelMap[category] = catMap;
|
|
}
|
|
|
|
writeFileSync(
|
|
'assets/beyond.yaml',
|
|
yaml.dump(levelMap, {
|
|
sortKeys: true,
|
|
flowLevel: 2,
|
|
}),
|
|
);
|
|
}
|
|
|
|
if (require.main === module) {
|
|
repairBeyond();
|
|
}
|