UNPKG

@rep2recall/r2r-sqlite

Version:
904 lines (903 loc) 33.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = __importDefault(require("fs")); const spark_md5_1 = __importDefault(require("spark-md5")); const quiz_1 = require("./quiz"); const q2filter_1 = __importStar(require("q2filter")); const v4_1 = __importDefault(require("uuid/v4")); const util_1 = require("./util"); const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify")); const liteorm_1 = __importStar(require("liteorm")); const r2r_format_1 = require("@rep2recall/r2r-format"); const valid_moment_1 = require("valid-moment"); let DbDeck = class DbDeck { }; __decorate([ liteorm_1.primary({ autoincrement: true }), __metadata("design:type", Number) ], DbDeck.prototype, "_id", void 0); __decorate([ liteorm_1.prop({ unique: true }), __metadata("design:type", String) ], DbDeck.prototype, "name", void 0); DbDeck = __decorate([ liteorm_1.Table({ name: "deck" }) ], DbDeck); let DbSource = class DbSource { }; __decorate([ liteorm_1.primary({ autoincrement: true }), __metadata("design:type", Number) ], DbSource.prototype, "_id", void 0); __decorate([ liteorm_1.prop({ unique: true }), __metadata("design:type", String) ], DbSource.prototype, "h", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", String) ], DbSource.prototype, "name", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", Date) ], DbSource.prototype, "created", void 0); DbSource = __decorate([ liteorm_1.Table({ name: "source" }) ], DbSource); let DbTemplate = class DbTemplate { }; __decorate([ liteorm_1.primary({ autoincrement: true }), __metadata("design:type", Number) ], DbTemplate.prototype, "_id", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", String) ], DbTemplate.prototype, "name", void 0); __decorate([ liteorm_1.prop({ references: "source(_id)", null: true }), __metadata("design:type", Number) ], DbTemplate.prototype, "sourceId", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", String) ], DbTemplate.prototype, "front", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", String) ], DbTemplate.prototype, "back", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", String) ], DbTemplate.prototype, "css", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", String) ], DbTemplate.prototype, "js", void 0); DbTemplate = __decorate([ liteorm_1.Table({ name: "template", unique: [["front", "back", "css", "js"]] }) ], DbTemplate); let DbNote = class DbNote { }; __decorate([ liteorm_1.primary({ autoincrement: true }), __metadata("design:type", Number) ], DbNote.prototype, "_id", void 0); __decorate([ liteorm_1.prop({ unique: true }), __metadata("design:type", String) ], DbNote.prototype, "key", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", String) ], DbNote.prototype, "name", void 0); __decorate([ liteorm_1.prop({ references: "source(_id)", null: true }), __metadata("design:type", Number) ], DbNote.prototype, "sourceId", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", Object) ], DbNote.prototype, "data", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", Object) ], DbNote.prototype, "order", void 0); DbNote = __decorate([ liteorm_1.Table({ name: "note" }) ], DbNote); let DbMedia = class DbMedia { }; __decorate([ liteorm_1.primary({ autoincrement: true }), __metadata("design:type", Number) ], DbMedia.prototype, "_id", void 0); __decorate([ liteorm_1.prop({ unique: true }), __metadata("design:type", String) ], DbMedia.prototype, "h", void 0); __decorate([ liteorm_1.prop({ references: "source(_id)", null: true }), __metadata("design:type", Number) ], DbMedia.prototype, "sourceId", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", String) ], DbMedia.prototype, "name", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", ArrayBuffer) ], DbMedia.prototype, "data", void 0); DbMedia = __decorate([ liteorm_1.Table({ name: "media" }) ], DbMedia); let DbCard = class DbCard { }; __decorate([ liteorm_1.primary(), __metadata("design:type", String) ], DbCard.prototype, "_id", void 0); __decorate([ liteorm_1.prop({ references: "deck(_id)" }), __metadata("design:type", Number) ], DbCard.prototype, "deckId", void 0); __decorate([ liteorm_1.prop({ references: "template(_id)", null: true }), __metadata("design:type", Number) ], DbCard.prototype, "templateId", void 0); __decorate([ liteorm_1.prop({ references: "note(_id)", null: true }), __metadata("design:type", Number) ], DbCard.prototype, "noteId", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", String) ], DbCard.prototype, "front", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", String) ], DbCard.prototype, "back", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", String) ], DbCard.prototype, "mnemonic", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", Number) ], DbCard.prototype, "srsLevel", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", Date) ], DbCard.prototype, "nextReview", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", Array) ], DbCard.prototype, "tag", void 0); __decorate([ liteorm_1.prop(), __metadata("design:type", Date) ], DbCard.prototype, "created", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", Date) ], DbCard.prototype, "modified", void 0); __decorate([ liteorm_1.prop({ null: true }), __metadata("design:type", Object) ], DbCard.prototype, "stat", void 0); DbCard = __decorate([ liteorm_1.Table({ name: "card" }) ], DbCard); class R2rSqlite extends r2r_format_1.R2rLocal { constructor(filename) { super(filename); } async build() { this.db = await liteorm_1.default.connect(this.filename); this.deck = await this.db.collection(new DbDeck()); this.source = await this.db.collection(new DbSource()); this.template = await this.db.collection(new DbTemplate()); this.note = await this.db.collection(new DbNote()); this.media = await this.db.collection(new DbMedia()); this.card = await this.db.collection(new DbCard()); const preNoteCreateOrUpdate = (entry) => { if (entry.data) { if (Array.isArray(entry.data)) { const { data, order } = r2r_format_1.fromSortedData(entry.data); entry.data = data; entry.order = order; } entry.key = spark_md5_1.default.hash(fast_json_stable_stringify_1.default(entry.data)); } }; this.note.on("pre-create", (p) => preNoteCreateOrUpdate(p.entry)); this.note.on("pre-update", (p) => preNoteCreateOrUpdate(p.set)); function preMediaCreateOrUpdate(entry) { if (entry.data && !entry.h) { entry.h = spark_md5_1.default.ArrayBuffer.hash(entry.data); } } this.media.on("pre-create", (p) => preMediaCreateOrUpdate(p.entry)); this.media.on("pre-update", (p) => preMediaCreateOrUpdate(p.set)); return this; } async close() { await this.db.close(); return this; } async reset() { await Promise.all([ this.source.delete({}), this.media.delete({}), this.template.delete({}), this.note.delete({}), this.card.delete({}), this.deck.delete({}) ]); return this; } async parseCond(q, options = {}) { if (options.sortBy === "random") { q += " is:random"; delete options.sortBy; } const parser = new q2filter_1.default(q, { anyOf: new Set(["template", "front", "mnemonic", "deck", "tag"]), isString: new Set(["template", "front", "back", "mnemonic", "deck", "tag"]), isDate: new Set(["created", "modified", "nextReview"]), transforms: { "is:due": () => { return { nextReview: { $lt: new Date() } }; } }, filters: { "is:distinct": (items) => { const col = {}; for (const it of items) { const k = it.key; if (k) { if (!col[k]) { col[k] = it; } } else { col[v4_1.default()] = it; } } return Object.values(col); }, "is:duplicate": (items) => { const col = {}; for (const it of items) { const k = it.front; col[k] = col[k] || []; col[k].push(it); } return Object.values(col).filter((a) => a.length > 1).reduce((a, b) => [...a, ...b], []); }, "is:random": (items) => { return util_1.shuffle(items); } }, sortBy: options.sortBy ? { key: options.sortBy, desc: options.desc !== undefined ? options.desc : true } : undefined }); const fullCond = parser.getCondFull(); if (!options.fields) { return { data: [], count: 0 }; } else if (options.fields === "*") { options.fields = ["data", "source", "deck", "front", "js", "mnemonic", "modified", "nextReview", "sCreated", "sH", "srsLevel", "stat", "tBack", "tFront", "tag", "template", "back", "created", "css", "_id"]; } const allFields = new Set(options.fields || []); for (const f of (fullCond.fields || [])) { allFields.add(f); } if (q.includes("is:distinct") || q.includes("is:duplicate")) { allFields.add("data"); } const select = {}; for (const f of allFields) { switch (f) { case "data": select.note = select.note || []; select.note.push(f, "order"); break; case "source": select.source = select.source || []; select.source.push("name"); break; case "sH": case "sCreated": select.source = select.source || []; select.source.push(f.substr(1).toLocaleLowerCase()); break; case "deck": select.deck = select.deck || []; select.deck.push("name"); break; case "tFront": case "tBack": select.template = select.template || []; select.template.push(f.substr(1).toLocaleLowerCase()); break; case "template": select.template = select.template || []; select.template.push("name"); break; case "css": case "js": select.template = select.template || []; select.template.push(f); break; default: select.card = select.card || []; select.card.push(f); } } let chain = this.card.chain(select.card); delete select.card; for (const [rName, rSelect] of Object.entries(select)) { let on = `${rName}Id`; switch (rName) { case "source": on = "note.sourceId"; } chain = chain.join(this[rName], on, "_id", rSelect, "left"); } const data = (await chain.data()).map((c) => { const { order, data } = c.note || {}; const output = { data: order && data ? r2r_format_1.toSortedData({ order, data }) : undefined, source: q2filter_1.dotGetter(c, "source.name"), sourceCreated: q2filter_1.dotGetter(c, "source.created"), sourceH: q2filter_1.dotGetter(c, "source.h"), deck: q2filter_1.dotGetter(c, "deck.name"), tFront: q2filter_1.dotGetter(c, "template.front"), tBack: q2filter_1.dotGetter(c, "template.back"), template: q2filter_1.dotGetter(c, "template.name"), css: q2filter_1.dotGetter(c, "template.css"), js: q2filter_1.dotGetter(c, "template.js"), front: q2filter_1.dotGetter(c, "card.front"), back: q2filter_1.dotGetter(c, "card.back"), mnemonic: q2filter_1.dotGetter(c, "card.mnemonic"), srsLevel: q2filter_1.dotGetter(c, "card.srsLevel"), nextReview: q2filter_1.dotGetter(c, "card.nextReview"), tag: q2filter_1.dotGetter(c, "card.tag"), created: q2filter_1.dotGetter(c, "card.created"), modified: q2filter_1.dotGetter(c, "card.modified"), stat: q2filter_1.dotGetter(c, "card.stat"), _id: q2filter_1.dotGetter(c, "card._id") }; return output; }); const cards = parser.parse(data); let endPoint; if (options.limit) { endPoint = (options.offset || 0) + options.limit; } return { data: cards.slice(options.offset || 0, endPoint), count: cards.length }; } async insertMany(entries) { entries = await Promise.all(entries.map((e) => this.transformCreateOrUpdate(null, e))); const now = new Date(); const sIdMap = {}; await entries.filter((e) => e.sH).distinctBy((e) => e.sH).mapAsync(async (el) => { await this.source.create({ name: el.source, created: (typeof el.sCreated === "string" ? valid_moment_1.toDate(el.sCreated) : el.sCreated) || now, h: el.sH }, true); sIdMap[el.sH] = (await this.source.get({ h: el.sH }, ["_id"]))._id; }); const tIdMap = {}; await entries.filter((el) => el.template).distinctBy((el) => el.template).mapAsync(async (el) => { const key = { front: el.tFront, back: el.tBack, css: el.css, js: el.js }; await this.template.create({ ...key, name: el.template, sourceId: el.sH ? sIdMap[el.sH] : undefined }, true); tIdMap[el.template] = (await this.template.get(key, ["_id"]))._id; }); const nIdMap = {}; await entries.filter((el) => el.data).distinctBy((el) => { el.key = spark_md5_1.default.hash(fast_json_stable_stringify_1.default(el.data)); return el.key; }).mapAsync(async (el) => { const { data, order } = r2r_format_1.fromSortedData(el.data); await this.note.create({ name: `${el.sH}/${el.template}/${el.data[0].value}`, data, order, sourceId: el.sH ? sIdMap[el.sH] : undefined }, true); nIdMap[el.key] = (await this.note.get({ data }, ["_id"]))._id; }); const dMap = {}; const decks = entries.map((e) => e.deck); const deckIds = await Promise.all(decks.map((d) => this.getOrCreateDeck(d))); decks.forEach((d, i) => { dMap[d] = deckIds[i]; }); const cIds = []; entries.map((e) => { const _id = v4_1.default(); cIds.push(_id); this.card.create({ _id, front: e.front, back: e.back, mnemonic: e.mnemonic, srsLevel: e.srsLevel, nextReview: (typeof e.nextReview === "string" ? valid_moment_1.toDate(e.nextReview) : e.nextReview) || undefined, deckId: dMap[e.deck], noteId: nIdMap[e.key], templateId: tIdMap[e.template], created: now, tag: e.tag }); }); return cIds; } async updateMany(ids, u) { if (ids.length > 900) { for (const idc of util_1.chunk(ids, 900)) { await this.updateMany(idc, u); } return; } const now = new Date(); const cs = await (await this.card.find({ _id: { $in: ids } }, ["_id", ...Object.keys(u)])) .mapAsync(async (c) => { const c0 = Object.assign(c, await this.transformCreateOrUpdate(c._id, u, now)); const c1 = { _id: c._id }; for (let [k, v] of Object.entries(c0)) { switch (k) { case "deck": k = "deckId"; v = await this.getOrCreateDeck(v); c1[k] = v; break; case "tFront": case "tBack": k = k.substr(1).toLocaleLowerCase(); case "css": case "js": const { templateId } = (await this.card.get({ _id: c._id }, ["templateId"])); await this.template.update({ _id: templateId }, { [k]: v }); break; case "data": const noteId = (await this.card.get({ _id: c._id }, ["noteId"])).noteId; const n = await this.note.get({ key: noteId }, ["order", "data"]); if (n) { const { order, data } = n; for (const { key, value } of v) { if (!order[key]) { order[key] = Math.max(...Object.values(order)) + 1; } data[key] = value; } await this.note.update({ key: noteId }, { order, data }); } else { const order = {}; const data = {}; for (const { key, value } of v) { if (!order[key]) { order[key] = Math.max(-1, ...Object.values(order)) + 1; } data[key] = value; } const key = this.getNoteKey(data); const name = `${key}/${Object.values(data)[0]}`; await this.note.create({ key, name, order, data }); c1.noteId = key; } break; default: c1[k] = v; } } return c1; }); for (const c of cs) { if (Object.keys(c).length > 1) { await this.card.update({ _id: c._id }, c); } } } async addTags(ids, tags) { if (ids.length > 900) { for (const idc of util_1.chunk(ids, 900)) { await this.addTags(idc, tags); } return; } const now = new Date(); await Promise.all((await this.card.find({ _id: { $in: ids } }, ["_id", "tag"])).map((c) => { c.modified = now; c.tag = c.tag || []; for (const t of tags) { if (!c.tag.includes(t)) { c.tag.push(t); } } return this.card.update({ _id: c._id }, c); })); } async removeTags(ids, tags) { if (ids.length > 900) { for (const idc of util_1.chunk(ids, 900)) { await this.removeTags(idc, tags); } return; } const now = new Date(); await Promise.all((await this.card.find({ _id: { $in: ids } }, ["_id", "tag"])).map((c) => { c.modified = now; const newTags = []; for (const t of (c.tag || [])) { if (!tags.includes(t)) { newTags.push(t); } } c.tag = newTags; return this.card.update({ _id: c._id }, c); })); } async deleteMany(ids) { if (ids.length > 900) { for (const idc of util_1.chunk(ids, 900)) { await this.deleteMany(idc); } return; } await this.card.delete({ _id: { $in: ids } }); } async render(cardId) { const r = await this.parseCond(`_id=${cardId}`, { limit: 1, fields: ["front", "back", "mnemonic", "tFront", "tBack", "data", "css", "js"] }); const c = r.data[0]; const { tFront, tBack, data } = c; if (/@md5\n/.test(c.front || "")) { c.front = r2r_format_1.ankiMustache(tFront || "", data); } if (c.back && /@md5\n/.test(c.back)) { c.back = r2r_format_1.ankiMustache(tBack || "", data, c.front); } return c; } async updateSrsLevel(dSrsLevel, cardId) { const card = await this.card.get({ _id: cardId }, ["srsLevel", "stat"]); if (!card) { return; } card.srsLevel = card.srsLevel || 0; card.stat = card.stat || { streak: { right: 0, wrong: 0 } }; card.stat.streak = card.stat.streak || { right: 0, wrong: 0 }; if (dSrsLevel > 0) { card.stat.streak.right = (card.stat.streak.right || 0) + 1; } else if (dSrsLevel < 0) { card.stat.streak.wrong = (card.stat.streak.wrong || 0) + 1; } card.srsLevel += dSrsLevel; if (card.srsLevel >= quiz_1.srsMap.length) { card.srsLevel = quiz_1.srsMap.length - 1; } if (card.srsLevel < 0) { card.srsLevel = 0; } if (dSrsLevel > 0) { card.nextReview = quiz_1.getNextReview(card.srsLevel); } else { card.nextReview = quiz_1.repeatReview(); } const { srsLevel, stat, nextReview } = card; await this.updateMany([cardId], { srsLevel, stat, nextReview }); } async transformCreateOrUpdate(cardId, u, timestamp = new Date()) { let data = null; let front = ""; if (!cardId) { u.created = timestamp; } else { u.modified = timestamp; } if (u.front && u.front.startsWith("@template\n")) { if (!data) { if (cardId) { data = await this.getData(cardId); } else { data = u.data || []; } } u.tFront = u.front.substr("@template\n".length); } if (u.tFront) { front = r2r_format_1.ankiMustache(u.tFront, data || []); u.front = "@md5\n" + spark_md5_1.default.hash(front); } if (u.back && u.back.startsWith("@template\n")) { if (!data) { if (cardId) { data = await this.getData(cardId); } else { data = u.data || []; } } u.tBack = (u.back || "").substr("@template\n".length); if (!front && cardId) { front = await this.getFront(cardId); } } if (u.tBack) { const back = r2r_format_1.ankiMustache(u.tBack, data || [], front); u.back = "@md5\n" + spark_md5_1.default.hash(back); } return u; } async getOrCreateDeck(name) { try { return await this.deck.create({ name }); } catch (e) { return (await this.deck.get({ name }, ["_id"]))._id; } } async getData(cardId) { const output = []; const c = await this.card.get({ _id: cardId }, ["noteId"]); if (c && c.noteId) { const n = await this.note.get({ key: c.noteId }, ["data", "order"]); if (n) { for (const [k, v] of Object.entries(n.data)) { output[n.order[k]] = { key: k, value: v }; } } } return output; } async getFront(cardId) { const c = await this.card.get({ _id: cardId }, ["front", "templateId"]); if (c && c.front) { if (c.front.startsWith("@md5\n") && c.templateId) { const t = await this.template.get({ name: c.templateId }, ["front"]); if (t) { const data = await this.getData(cardId); return r2r_format_1.ankiMustache(t.front, data || []); } } return c.front; } return ""; } async fromR2r(r2r, options) { const filename = options ? options.filename : undefined; const callback = options ? options.callback : undefined; if (callback) callback({ text: "Reading R2r file" }); const data = fs_1.default.readFileSync(r2r.filename); const sourceH = spark_md5_1.default.ArrayBuffer.hash(data); const now = new Date(); let sourceId; try { sourceId = await this.source.create({ name: filename || r2r.filename, h: sourceH, created: now }); } catch (e) { if (callback) callback({ text: "Duplicated resource" }); return; } await (await r2r.allMedia()).mapAsync((m) => { return this.media.create({ name: m.name, data: m.data, sourceId }, true); }); const cs = await r2r.parseCond("", { fields: "*" }); await r2r.insertMany(cs.data); } async export(r2r, q = "", options) { const callback = options ? options.callback : undefined; let current = 1; const ms = await this.media.find({}); for (const m of ms) { if (callback) callback({ text: "Inserting media", current, max: ms.length }); try { await r2r.createMedia(m); } catch (e) { } current++; } if (callback) callback({ text: "Parsing q" }); const cs = await this.parseCond(q, { fields: "*" }); current = 1; for (const c of util_1.chunk(cs.data, 1000)) { if (callback) callback({ text: "Inserting cards", current, max: cs.count }); await r2r.insertMany(c); current += 1000; } await r2r.close(); } async getMedia(h) { const m = await this.media.get({ h }); return m || null; } async allMedia() { return await this.media.find({}); } async createMedia(m) { const h = spark_md5_1.default.ArrayBuffer.hash(m.data); await this.media.create({ ...m, h }); return h; } async deleteMedia(h) { await this.media.delete({ h }); return true; } async fromAnki(anki, options) { const filename = options ? options.filename : undefined; const callback = options ? options.callback : undefined; if (callback) callback({ text: "Reading Anki file" }); const data = fs_1.default.readFileSync(anki.filePath); const now = new Date(); let sourceId; const sourceH = spark_md5_1.default.ArrayBuffer.hash(data); try { sourceId = await this.source.create({ name: filename || anki.filePath, h: sourceH, created: now }); } catch (e) { if (callback) callback({ text: "Duplicated Anki resource" }); return; } let current; let max; const media = await anki.apkg.tables.media.all(); current = 0; max = media.length; await media.mapAsync(async (el) => { if (callback) callback({ text: "Inserting media", current, max }); await this.media.create({ h: el.h, name: el.name, data: el.data, sourceId }, true); current++; }); const card = await anki.apkg.tables.cards.all(); const dIdMap = {}; const tIdMap = {}; const nIdMap = {}; current = 1; max = card.length; for (const c of util_1.chunk(card, 1000)) { if (callback) callback({ text: "Inserting cards", current, max }); await c.mapAsync(async (el) => { if (!Object.keys(dIdMap).includes(el.deck.name)) { const name = el.deck.name; await this.deck.create({ name }, true); dIdMap[name] = (await this.deck.get({ name }, ["_id"]))._id; } const t = { name: `${sourceH}/${el.note.model.name}/${el.template.name}`, front: el.template.qfmt, back: el.template.afmt, css: el.note.model.css }; const templateKey = this.getTemplateKey(t); if (!Object.keys(tIdMap).includes(templateKey)) { await this.template.create({ ...t, sourceId }, true); tIdMap[templateKey] = (await this.template.get(t, ["_id"]))._id; } const data = {}; const order = {}; el.template.model.flds.forEach((k, i) => { data[k] = el.note.flds[i]; order[k] = i; }); const key = this.getNoteKey(data); if (!Object.keys(nIdMap).includes(key)) { await this.note.create({ key, name: `${sourceH}/${el.note.model.name}/${el.template.name}/${el.note.flds[0]}`, data, order, sourceId }, true); nIdMap[key] = (await this.note.get({ key }, ["_id"]))._id; } const front = r2r_format_1.ankiMustache(el.template.qfmt, data); const back = r2r_format_1.ankiMustache(el.template.afmt, data, front); await this.card.create({ _id: v4_1.default(), deckId: dIdMap[el.deck.name], templateId: tIdMap[templateKey], noteId: nIdMap[key], front: `@md5\n${spark_md5_1.default.hash(front)}`, back: `@md5\n${spark_md5_1.default.hash(back)}`, created: now, tag: el.note.tags }, true); }); current += 1000; } ; } } exports.default = R2rSqlite;