py/gadialog_srs_anki.py
author Oleksandr Gavenko <gavenkoa@gmail.com>
Fri, 21 Aug 2020 16:05:06 +0300
changeset 1228 e38d1d191339
parent 1223 d592572cc546
permissions -rw-r--r--
Fixed import.

# -*- coding: utf-8 -*-
"""Anki card writer from gaphrase format"""

import re
import hashlib

import os
import io
import sys
import tempfile
import shutil

from gadialog import Parser

import anki
from anki.exporting import AnkiPackageExporter

FINAME = None
FONAME = None
FDELNAME = None

ARG_NAME_RE = re.compile("-name=(.+)")
ARG_DELFILE_RE = re.compile("-delfile=(.+)")

look_for_files = False
for idx in range(1, len(sys.argv)):
    arg = sys.argv[idx]
    if arg == "--":
        look_for_files = True
        continue
    if not look_for_files:
        m = ARG_NAME_RE.match(arg)
        if m:
            NAME = m.group(1)
            continue
        m = ARG_DELFILE_RE.match(arg)
        if m:
            FDELNAME = m.group(1)
            continue
        if arg.startswith("-"):
            raise Exception("Unsupported option format: '{:s}'".format(arg))
    if not FINAME:
        FINAME = arg
        continue
    if not FONAME:
        FONAME = arg
        continue
    raise Exception("Unnecessary argument: '{:s}'".format(arg))

if not FINAME:
    raise Exception("Input file name is not passed...")
if FONAME is None:
    raise Exception("Output file name is not passed...")
if not NAME:
    NAME, _ = os.path.splitext(os.path.basename(FINAME))

# if FDELNAME:
#     FDEL = io.open(FDELNAME, mode='r', buffering=1, encoding="utf-8")
# else:
#     FDEL = None

################################################################

FIN = io.open(FINAME, mode='r', buffering=1, encoding="utf-8")

PARSER = Parser()
try:
    DOM = PARSER.parse(FIN)
finally:
    FIN.close()

################################################################

MODEL_CSS = """
.card {
  font-family: arial;
  font-size: 20px;
  text-align: left;
  color: black;
  background-color: white;
}
.line {
  margin-bottom: 0.5em;
}
.odd {
  color: #004000;
}
.even {
  color: #000080;
}
"""

class AnkiDbBuilder:

    def __init__(self, tmpdir, name):
        self.tmpdir = tmpdir
        self.name = name

        self.collection = collection = anki.Collection(os.path.join(self.tmpdir, 'collection.anki2'))

        deck_id = collection.decks.id(self.name)

        # It is essential to keep model['id'] unchanged between upgrades!!
        model_id = int(hashlib.sha1(self.name.encode('utf-8')).hexdigest(), 16) % (2**63)

        ################################################################
        # One face card model.
        model = collection.models.new(self.name + "_front")
        model['did'] = deck_id
        model['css'] = MODEL_CSS

        collection.models.addField(model, collection.models.newField('Front'))

        tmpl = collection.models.newTemplate('Front')
        tmpl['qfmt'] = '<div class="front">{{Front}}</div>'
        tmpl['afmt'] = '{{FrontSide}}'
        collection.models.addTemplate(model, tmpl)

        model['id'] = model_id
        collection.models.update(model)
        collection.models.save(model)
        self.model = model

    def guid(self, num):
        h = hashlib.md5(":".join((self.name, str(num))).encode('utf-8'))
        return h.hexdigest()

    def add_note(self, num, front):
        note = anki.notes.Note(self.collection, self.model)
        note['Front'] = front
        note.guid = self.guid(num)
        self.collection.addNote(note)

    def export(self, fname):
        export = AnkiPackageExporter(self.collection)
        export.exportInto(fname)

    def close(self):
        self.collection.close()

def write_lines(buf, lines):
    odd = True
    for line in lines:
        if odd:
            buf.append("<div class='line odd'>")
        else:
            buf.append("<div class='line even'>")
        buf.append("- ")
        buf.append(line)
        buf.append("</div>")
        odd = not odd

# Looks like anki libs change working directory to media directory of current deck
# Therefore absolute path should be stored before creating temporary deck
FONAME = os.path.abspath(FONAME)
TMPDIR = tempfile.mkdtemp(dir=os.path.dirname(FONAME))

try:
    BUILDER = AnkiDbBuilder(TMPDIR, NAME)

    for num, lines in DOM.items():
        buf = []
        write_lines(buf, lines)
        front = "".join(buf)
        BUILDER.add_note(num, front)
    BUILDER.export(FONAME)
finally:
    BUILDER.close()
    shutil.rmtree(TMPDIR, ignore_errors=True)