diff --git a/scripts/populate-from-wanikani.ts b/scripts/populate-from-wanikani.ts
index 87366f6..da89b3f 100644
--- a/scripts/populate-from-wanikani.ts
+++ b/scripts/populate-from-wanikani.ts
@@ -16,6 +16,7 @@ async function main() {
sentenceJa: 'Sentence',
sentenceAudio: 'SentenceAudio',
sentenceEn: 'SentenceMeaning',
+ sentenceCloze: 'MeaningQuiz',
},
{ overwrite: true },
);
diff --git a/scripts/query-anki.ts b/scripts/query-anki.ts
index 0f5d503..f817537 100644
--- a/scripts/query-anki.ts
+++ b/scripts/query-anki.ts
@@ -1,5 +1,5 @@
import { soundTag } from '@/anki';
-import { AnkiConnect } from '@/ankiconnect';
+import { AnkiConnect, IAnkiConnectActions } from '@/ankiconnect';
export async function listTags(
query = 'deck:Takoboto -Takoboto: tag:common -tag:reading-challenge -tag:favorites -tag:death-note -tag:ハピネス -tag:https://www.bunka.go.jp/seisaku/bunkashingikai/kokugo/hokoku/pdf/ijidokun_140221.pdf',
@@ -63,6 +63,90 @@ export async function addSoundTag(query: string, fieldNames: string[]) {
});
}
+export async function fixCloze(
+ query: string,
+ fields: { vocabJa: string; sentenceCloze: string },
+) {
+ const anki = new AnkiConnect();
+ anki
+ .api('findNotes', {
+ query,
+ })
+ .then((notes) => anki.api('notesInfo', { notes }))
+ .then(async (notes) => {
+ const notesToUpdate: IAnkiConnectActions['updateNoteFields']['params']['note'][] =
+ [];
+
+ const badJa: string[] = [];
+
+ for (const n of notes) {
+ const { value: ja } = n.fields[fields.vocabJa] || {};
+ const { value: sent } = n.fields[fields.sentenceCloze] || {};
+ if (ja && sent) {
+ const clozeChar = '__';
+
+ if (
+ sent
+ .split('
')
+ .filter((_, i) => i % 2 === 0)
+ .every((s) => s.includes(clozeChar))
+ ) {
+ continue;
+ }
+
+ const cleanJa = ja.replace(/\[.+?\]/g, '').replace(/ /g, '');
+ let newSent = sent.replace(cleanJa, clozeChar);
+
+ const jaNoOkuri = cleanJa.replace(/[\p{sc=Hiragana}]/gu, '');
+ if (cleanJa !== jaNoOkuri && newSent.includes(jaNoOkuri)) {
+ const s2 = newSent.replace(jaNoOkuri, clozeChar);
+ if (s2 !== newSent) {
+ newSent = s2;
+ } else {
+ badJa.push(ja);
+ }
+ }
+
+ if (!sent.includes(clozeChar) && sent !== newSent) {
+ notesToUpdate.push({
+ id: n.noteId,
+ fields: {
+ [fields.sentenceCloze]: newSent,
+ },
+ });
+ }
+ }
+ }
+
+ while (badJa.length) {
+ console.log(
+ query +
+ ' tag:wanikani card:EJ (' +
+ badJa
+ .splice(0, 20)
+ .map((ja) => `${fields.vocabJa}:${ja}`)
+ .join(' OR ') +
+ ')',
+ );
+ }
+
+ if (!notesToUpdate.length) return;
+
+ await anki.multi<'updateNoteFields'[]>({
+ actions: notesToUpdate.map((note) => ({
+ action: 'updateNoteFields',
+ params: {
+ note,
+ },
+ })),
+ });
+ });
+}
+
if (require.main === module) {
- addSoundTag('note:jp.takoboto', ['SentenceAudio', 'JapaneseAudio']);
+ // addSoundTag('note:jp.takoboto', ['SentenceAudio', 'JapaneseAudio']);
+ fixCloze('deck:Takoboto', {
+ vocabJa: 'Japanese',
+ sentenceCloze: 'MeaningQuiz',
+ });
}
diff --git a/src/wanikani.ts b/src/wanikani.ts
index 7b3846e..3200f4d 100644
--- a/src/wanikani.ts
+++ b/src/wanikani.ts
@@ -413,6 +413,7 @@ export class WaniKani {
sentenceJa: string;
sentenceAudio?: string;
sentenceEn: string;
+ sentenceCloze?: string;
},
opts: {
overwrite?: boolean;
@@ -428,13 +429,20 @@ export class WaniKani {
{
ja: string;
en: string;
+ sentences: {
+ ja: string;
+ en: string;
+ }[];
}
>();
vocabularies.map((v) => {
if (sentenceMap.has(v.data.characters)) return;
const sent = v.data.context_sentences[0];
if (!sent || !sent.ja.trim()) return;
- sentenceMap.set(v.data.characters, sent);
+ sentenceMap.set(v.data.characters, {
+ ...sent,
+ sentences: v.data.context_sentences,
+ });
});
if (!sentenceMap.size) return;
@@ -459,16 +467,27 @@ export class WaniKani {
for (const n of notes) {
const { value: ja } = n.fields[fields.vocabJa] || {};
if (ja) {
- const sent = sentenceMap.get(
- ja.replace(/\[.+?\]/g, '').replace(/ /g, ''),
- );
+ const cleanJa = ja.replace(/\[.+?\]/g, '').replace(/ /g, '');
+ const sent = sentenceMap.get(cleanJa);
if (sent) {
+ const fieldUpdate = {
+ [fields.sentenceJa]: sent.ja,
+ [fields.sentenceEn]: sent.en,
+ };
+
+ if (
+ fields.sentenceCloze &&
+ !n.fields[fields.sentenceCloze]?.value
+ ) {
+ fieldUpdate[fields.sentenceCloze] = sent.sentences
+ .map(({ ja, en }) => `${ja}
${en}`)
+ .join('
')
+ .replace(cleanJa, '__');
+ }
+
notesToUpdate.push({
id: n.noteId,
- fields: {
- [fields.sentenceJa]: sent.ja,
- [fields.sentenceEn]: sent.en,
- },
+ fields: fieldUpdate,
});
}
}