feat(anki): basic cli using typer

This commit is contained in:
arne314
2024-12-22 22:53:10 +01:00
parent ac353e5c74
commit 1124181f59
6 changed files with 120 additions and 17 deletions

View File

@@ -14,6 +14,7 @@ requires-python = ">=3.9.20"
dependencies = [
"aiohttp>=3.11.11",
"tree-sitter-language-pack>=0.2.0",
"typer>=0.15.1",
]
[project.scripts]

View File

@@ -26,6 +26,7 @@ class AnkiConnectApi:
add.append(card)
else:
update.append(card)
print(f"Pushing {len(add)} new flashcards and {len(update)} updated flashcards to Anki...")
await asyncio.gather(self._add(add), self._update(update))
async def _request_api(self, action, **params):

View File

@@ -34,7 +34,7 @@ class Flashcard:
def as_html(self, front: bool) -> str:
prefix = f"<p hidden>{self.front}: {self.back}{' ' * 10}</p>" # indexable via anki search
image = f'<img src="{self.svg_filename(front)}" width=100 />'
image = f'<img src="{self.svg_filename(front)}" />'
return prefix + image
def as_anki_model(self, tmp: bool = False) -> dict:

View File

@@ -1,34 +1,37 @@
import asyncio
import glob
import os
from typing_extensions import Annotated
import typer
from anki.anki_api import AnkiConnectApi
from anki.file_handler import FileHandler
from anki.parser import FlashcardParser
from anki.typst_compiler import TypstCompiler
cli = typer.Typer(name="typstar-anki")
async def export_flashcards(root_dir, typst_cmd):
parser = FlashcardParser()
compiler = TypstCompiler(os.getcwd())
compiler = TypstCompiler(root_dir, typst_cmd)
api = AnkiConnectApi()
async def export_flashcards(path):
# parse flashcards
print("Parsing flashcards...")
flashcards = []
file_handlers = []
for file in glob.glob(f"{path}/**/*.typ", recursive=True):
for file in glob.glob(f"{root_dir}/**/*.typ", recursive=True):
fh = FileHandler(file)
cards = parser.parse_file(fh)
file_handlers.append((fh, cards))
flashcards.extend(cards)
# async typst compilation
print("Compiling flashcards...")
await compiler.compile_flashcards(flashcards)
# async anki push per deck
print("Pushing flashcards to anki...")
await api.push_flashcards(flashcards)
# write id updates to files
@@ -44,8 +47,15 @@ async def export_flashcards(path):
print("Done")
@cli.command()
def cmd(root_dir: Annotated[
str, typer.Option(help="Directory scanned for flashcards and passed over to typst compile command")] = os.getcwd(),
typst_cmd: Annotated[str, typer.Option(help="Typst command used for flashcard compilation")] = "typst"):
asyncio.run(export_flashcards(root_dir, typst_cmd))
def main():
asyncio.run(export_flashcards(os.getcwd()))
typer.run(cmd)
if __name__ == "__main__":

View File

@@ -5,7 +5,8 @@ from typing import List
from .flashcard import Flashcard
default_preamble = """
#set page(width: auto, height: auto, margin: (rest: 0%))
#set text(size: 20pt)
#set page(width: auto, height: auto, margin: (rest: 8pt))
#let flashcard(id, front, back) = {
strong(front)
[\\ ]
@@ -24,7 +25,7 @@ class TypstCompiler:
typst_root_dir: str
max_processes: int
def __init__(self, typst_root_dir: str = ".", typst_cmd: str = "typst", preamble: str = None):
def __init__(self, typst_root_dir: str, typst_cmd: str, preamble: str = None):
if preamble is None:
preamble = default_preamble
self.typst_cmd = typst_cmd
@@ -39,12 +40,10 @@ class TypstCompiler:
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
proc.stdin.write(bytes(src, encoding="utf-8"))
proc.stdin.close()
await proc.wait()
if err := await proc.stderr.read():
raise TypstCompilationError(bytes.decode(err, encoding="utf-8"))
return await proc.stdout.read()
stdout, stderr = await proc.communicate(bytes(src, encoding="utf-8"))
if stderr:
raise TypstCompilationError(bytes.decode(stderr, encoding="utf-8"))
return stdout
async def _compile_flashcard(self, card: Flashcard):
front = await self._compile(self.preamble + "\n" + card.as_typst(True))
@@ -52,6 +51,7 @@ class TypstCompiler:
card.set_svgs(front, back)
async def compile_flashcards(self, cards: List[Flashcard]):
print(f"Compiling {len(cards)} flashcards...")
semaphore = asyncio.Semaphore(self.max_processes)
async def compile_coro(card):

91
uv.lock generated
View File

@@ -133,6 +133,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "frozenlist"
version = "1.5.0"
@@ -226,6 +247,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "multidict"
version = "6.1.0"
@@ -402,6 +444,38 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 },
]
[[package]]
name = "pygments"
version = "2.18.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
]
[[package]]
name = "rich"
version = "13.9.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
]
[[package]]
name = "tree-sitter"
version = "0.23.2"
@@ -555,6 +629,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/32/af2d676b0176a958f22a75b04be836e09476a10844baab78c018a5030297/tree_sitter_yaml-0.7.0-cp39-abi3-win_arm64.whl", hash = "sha256:f0f8d8e05fa8e70f08d0f18a209d6026e171844f4ea7090e7c779b9c375b3a31", size = 43650 },
]
[[package]]
name = "typer"
version = "0.15.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "rich" },
{ name = "shellingham" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/dca7b219718afd37a0068f4f2530a727c2b74a8b6e8e0c0080a4c0de4fcd/typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a", size = 99789 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/cc/0a838ba5ca64dc832aa43f727bd586309846b0ffb2ce52422543e6075e8a/typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847", size = 44908 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
@@ -571,12 +660,14 @@ source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
{ name = "tree-sitter-language-pack" },
{ name = "typer" },
]
[package.metadata]
requires-dist = [
{ name = "aiohttp", specifier = ">=3.11.11" },
{ name = "tree-sitter-language-pack", specifier = ">=0.2.0" },
{ name = "typer", specifier = ">=0.15.1" },
]
[[package]]