Skip to content
Snippets Groups Projects
Commit 2027cfad authored by Philipp Pospischil's avatar Philipp Pospischil
Browse files

chore: initial commit

parents
No related branches found
No related tags found
No related merge requests found
.parcel-cache
dist
temporary.media
temporary.anki2
temporary.media.db2
cool_new_template.apkg
cool_new_template_from_scratch.apkg
node_modules
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
anki = "==25.2"
libfaketime = "==3.0.0"
This diff is collapsed.
# Solution for Anki scripting crimes, Part 1: Managing Anki templates with python code
Hi there, you probably came here from [Commiting scripting crimes in Anki, Part 2: Managing Anki templates with python code](https://tapirbug.xyz/en/post/anki-scripting-crimes-part-2).
This repository contains the complete source code that we built together over
the article.
import HanziWriter from "hanzi-writer"
const kanji = document.getElementById("kanji").innerText
console.error("kanji: " + kanji)
const hanziWriterContainer = document.createElement("div")
document.body.appendChild(hanziWriterContainer)
HanziWriter.create(hanziWriterContainer, kanji).animateCharacter()
{
"replacements": {
"Front": "sake",
"Back": "酒"
}
}
\ No newline at end of file
{{FrontSide}}
<hr id="answer"><hr id="answer"><hr id="answer"><hr id="answer">
<div id="kanji">{{Back}}</div>
<script type="module">
import "./animation.js"
</script>
from anki.collection import *
from anki.import_export_pb2 import *
import os
# Step 1: create a temporary collection
try:
# delete if exists already
os.remove("temporary.anki2")
except OSError:
pass
# then start fresh
col = Collection("temporary.anki2")
# Step 2: import the template APKG with the note type, so that you can add data
col.import_anki_package(ImportAnkiPackageRequest(
# this is what you exported from Anki
package_path="reference.apkg",
# the options don't matter that much in our case
# since the collection we import into is empty anyway
options=ImportAnkiPackageOptions(
merge_notetypes=True,
update_notes=ImportAnkiPackageUpdateCondition.IMPORT_ANKI_PACKAGE_UPDATE_CONDITION_ALWAYS,
update_notetypes=ImportAnkiPackageUpdateCondition.IMPORT_ANKI_PACKAGE_UPDATE_CONDITION_NEVER,
with_scheduling=False,
with_deck_configs=False,
)
))
## Step 3: find the note type id we want to modify, and get the dictionary
note_type_id_for_template_replacement = None
for note_type in col.models.all_names_and_ids():
if note_type.name == "Japanese vocabulary":
note_type_id_for_template_replacement = note_type.id
if note_type_id_for_template_replacement is None:
raise Exception("Note type to modify templates of not found")
## Step 4: get the current note type dict
note_type = col.models.get(note_type_id_for_template_replacement)
## Step 5: set your one template (or more, in the same way)
with open("dist/front.html", "r") as front_file:
with open("dist/back.html") as back_file:
front_html = front_file.read()
back_html = back_file.read()
note_type["tmpls"][0]["qfmt"] = front_html
note_type["tmpls"][0]["afmt"] = back_html
## Step 5: write your changes back to the Anki collection
col.models.update_dict(note_type)
# Step 6: Done, now we just need to export an APKG
col.export_anki_package(
# anki fails if this is just an apkg filename without a dir,
# with ./ it's fine
out_path="./cool_new_template.apkg",
options=ExportAnkiPackageOptions(
with_deck_configs=False,
with_media=True,
with_scheduling=False,
legacy=True,
),
limit=None)
from anki.collection import *
from anki.import_export_pb2 import *
import os
from libfaketime import fake_time, reexec_if_needed
reexec_if_needed() # setup for faking the time later, which may require a restart
# Step 1: create a temporary collection
try:
# delete if exists already
os.remove("temporary.anki2")
except OSError:
pass
# then start fresh
col = Collection("temporary.anki2")
# Step 2: add a new note type
with open("dist/front.html", "r") as front_file:
with open("dist/back.html") as back_file:
front_html = front_file.read()
back_html = back_file.read()
note_type = {
'id': 0, # the 0 has no effect, the actual ID will be the timestamp at the time of adding the note type
'name': "My cool note type",
'type': 0,
'mod': 0,
'usn': 0,
'sortf': 0,
'did': None,
'tmpls': [
{
'name': 'English to Japanese',
'ord': 0,
'qfmt': front_html,
'afmt': back_html,
'bqfmt': '',
'bafmt': '',
'did': None, # this could maybe be the deck override option
'bfont': '',
'bsize': 0,
}
],
'flds': [
# Example
{
'name': "Front",
'ord': 0, # Field No.1, this one must always be non-empty
# not sure what this stuff does, just use CSS for styling
'sticky': False,
'rtl': False,
'font': 'Arial',
'size': 20,
'description': '',
'plainText': False,
'collapsed': False,
'excludeFromSearch': False,
'id': 0,
'tag': None,
'preventDeletion': False
},
{
'name': "Back",
'ord': 1, # Field No.2, can be empty
'sticky': False,
'rtl': False,
'font': 'Arial',
'size': 20,
'description': '',
'plainText': False,
'collapsed': False,
'excludeFromSearch': False,
'id': 1,
'tag': None,
'preventDeletion': False
}
# … more fields like Rōmaji
],
'css': '', # instead of putting it into the HTML you could also set CSS here
# note sure what the rest does
'latexPre':'\\documentclass[12pt]{article}\n\\special{papersize=3in,5in}\n\\usepackage[utf8]{inputenc}\n\\usepackage{amssymb,amsmath}\n\\pagestyle{empty}\n\\setlength{\\parindent}{0in}\n\\begin{document}\n',
'latexPost':'\\end{document}',
'latexSvg': False,
'req': [
[
0,
'any',
[ 0 ]
]
],
'originalStockKind': 1
}
# Step 3: add the note type with a predictable ID
with fake_time("2025-04-07 18:45:00"):
# add the note in the past (or the future)
note_type_id = col.models.add_dict(note_type).id
# Step 4: add a card and deck to have something to export
note = Note(col, note_type_id)
# you need some sort of unique ID for Anki to know on the next import if you
#already have the note or not
note.guid = "test1"
# set values from the CSV, using the name of the field you chose in Anki
note["Front"] = "tree"
note["Back"] = ""
deck_id = col.decks.add_normal_deck_with_name("Test deck").id
col.add_note(note, deck_id) # save the note to the collection
# Step 5: Done, now we just need to export an APKG
col.export_anki_package(
# anki fails if this is just an apkg filename without a dir,
# with ./ it's fine
out_path="./cool_new_template_from_scratch.apkg",
options=ExportAnkiPackageOptions(
with_deck_configs=False,
with_media=True,
with_scheduling=False,
legacy=True,
),
limit=None)
\ No newline at end of file
Translate to Kanji please:<br>
{{Front}}
This diff is collapsed.
{
"name": "anki-test",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "commonjs",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"hanzi-writer": "^3.7.2"
},
"devDependencies": {
"parcel": "^2.14.4",
"parcel-transformer-anki-tags": "^0.1.2"
},
"@parcel/bundler-default": {
"minBundles": 10000
}
}
{
"extends": "@parcel/config-default",
"transformers": {
"*.html": [ "parcel-transformer-anki-tags", "..." ]
}
}
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment