mirror of
https://github.com/Ascyii/typstar.git
synced 2026-01-01 13:34:24 -05:00
feat(anki): basic cli using typer
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
parser = FlashcardParser()
|
||||
compiler = TypstCompiler(os.getcwd())
|
||||
api = AnkiConnectApi()
|
||||
cli = typer.Typer(name="typstar-anki")
|
||||
|
||||
|
||||
async def export_flashcards(path):
|
||||
async def export_flashcards(root_dir, typst_cmd):
|
||||
parser = FlashcardParser()
|
||||
compiler = TypstCompiler(root_dir, typst_cmd)
|
||||
api = AnkiConnectApi()
|
||||
|
||||
# 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__":
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user