@rep2recall/r2r-sqlite
Version:
R2r format engine for Rep2Recall
904 lines (903 loc) • 33.6 kB
JavaScript
"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 (/\n/.test(c.front || "")) {
c.front = r2r_format_1.ankiMustache(tFront || "", data);
}
if (c.back && /\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: `\n${spark_md5_1.default.hash(front)}`,
back: `\n${spark_md5_1.default.hash(back)}`,
created: now,
tag: el.note.tags
}, true);
});
current += 1000;
}
;
}
}
exports.default = R2rSqlite;