feat: add generate python script.
This commit is contained in:
commit
de92476ccb
3 changed files with 127 additions and 0 deletions
120
generate.py
Executable file
120
generate.py
Executable file
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import click
|
||||
import genanki
|
||||
import markdown
|
||||
import pathlib
|
||||
import random
|
||||
import re
|
||||
|
||||
RE_DIRNAME = re.compile(r'^(.*)\.([0-9]+)\.([0-9]+)$')
|
||||
RE_FILENAME = re.compile(r'^(.*)\.([0-9]+)\.md$')
|
||||
|
||||
class MyNote(genanki.Note):
|
||||
pass
|
||||
|
||||
def load_notes(
|
||||
*,
|
||||
path: pathlib.Path,
|
||||
model: genanki.Model,
|
||||
) -> list[genanki.Note]:
|
||||
files_loaded = []
|
||||
final_files: dict[str, list[tuple[pathlib.Path, int]]] = {}
|
||||
for dirpath, _, filenames in path.walk():
|
||||
files_loaded += [dirpath / x for x in filenames]
|
||||
print(files_loaded)
|
||||
for f in files_loaded:
|
||||
match = RE_FILENAME.match(f.name)
|
||||
if match:
|
||||
final_files.setdefault(match.group(1), []).append((f, int(match.group(2))))
|
||||
notes: list[genanki.Note] = []
|
||||
for v in final_files.values():
|
||||
note_files: list[pathlib.Path] = [v_[0] for v_ in sorted(v, key=lambda x: x[1])]
|
||||
note_str: list[str] = []
|
||||
for f in note_files:
|
||||
with f.open() as fp:
|
||||
html_str = markdown.markdown(fp.read())
|
||||
note_str += [html_str]
|
||||
notes += [MyNote(model=model, fields=note_str, guid=genanki.guid_for(note_str[0]))]
|
||||
return notes
|
||||
|
||||
@click.command()
|
||||
@click.option('--name', '-n', 'name', type=str, default=None)
|
||||
@click.option('--model-name', '-m', 'model_name', type=str, default='Simple Model')
|
||||
@click.option('--template-name', '-t', 'template_name', type=str, default='Card 1')
|
||||
@click.option('--field', '-f', 'fields', type=str, default=['Question', 'Answer'], multiple=True)
|
||||
@click.option('--qfmt', '-q', 'question_format', type=str, default='{{Question}}')
|
||||
@click.option('--afmt', '-a', 'answer_format', type=str, default='{{FrontSide}}<hr id="answer">{{Answer}}')
|
||||
@click.option('--model-id', 'model_id', type=int, default=None)
|
||||
@click.option('--deck-id', 'deck_id', type=int, default=None)
|
||||
@click.option('--destructive/--no-destructive', 'destructive', type=bool, default=False, help='Perform write operations, which may end in data loss')
|
||||
@click.option('--autogenerate/--no-autogenerate', 'autogenerate', type=bool, default=False, help='Generate missing ids, should probably be used with --destructive, to always use the same ones.')
|
||||
@click.option('--debug/--no-debug', 'debug', type=bool, default=False, help="Debug mode, don't write files")
|
||||
@click.argument('path', type=click.Path(path_type=pathlib.Path, dir_okay=True))
|
||||
def main(
|
||||
*,
|
||||
name: str | None,
|
||||
model_name: str,
|
||||
template_name: str,
|
||||
fields: list[str],
|
||||
question_format: str,
|
||||
answer_format: str,
|
||||
model_id: int | None,
|
||||
deck_id: int | None,
|
||||
destructive: bool,
|
||||
autogenerate: bool,
|
||||
debug: bool,
|
||||
path: pathlib.Path,
|
||||
):
|
||||
match = RE_DIRNAME.match(path.name)
|
||||
if match:
|
||||
model_name = model_name or match.group(1)
|
||||
name = name or match.group(1)
|
||||
model_id = model_id or int(match.group(2))
|
||||
deck_id = deck_id or int(match.group(3))
|
||||
else:
|
||||
model_name = model_name or path.name
|
||||
name = name or path.name
|
||||
if model_id is None:
|
||||
if autogenerate:
|
||||
model_id = random.randrange(1 << 30, 1 << 31)
|
||||
else:
|
||||
raise click.ClickException('model_id could not be derived from directory name and is not explicitly set, or autogenerate enabled')
|
||||
if deck_id is None:
|
||||
if autogenerate:
|
||||
deck_id = random.randrange(1 << 30, 1 << 31)
|
||||
else:
|
||||
raise click.ClickException('deck_id could not be derived from directory name and is not explicitly set, or autogenerate enabled')
|
||||
if destructive:
|
||||
new_path = path.parent / f'{path.name}.{model_id}.{deck_id}'
|
||||
if not debug:
|
||||
path.rename(new_path)
|
||||
path = new_path
|
||||
model = genanki.Model(
|
||||
model_id,
|
||||
model_name,
|
||||
fields=[dict(name=x) for x in fields],
|
||||
templates=[
|
||||
{
|
||||
'name': template_name,
|
||||
'qfmt': question_format,
|
||||
'afmt': answer_format,
|
||||
},
|
||||
],
|
||||
)
|
||||
notes: list[genanki.Note] = load_notes(path=path, model=model)
|
||||
deck = genanki.Deck(
|
||||
deck_id,
|
||||
name,
|
||||
)
|
||||
for n in notes:
|
||||
deck.add_note(n)
|
||||
click.echo(f'added {len(notes)} notes')
|
||||
if not debug:
|
||||
genanki.Package(deck).write_to_file(path.parent / (path.name + '.apkg'))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
4
pyrightconfig.json
Normal file
4
pyrightconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"venvPath": ".",
|
||||
"venv": ".venv"
|
||||
}
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
click
|
||||
genanki
|
||||
markdown
|
Loading…
Add table
Reference in a new issue