From b716795d96d70f67ef8b2d5dde2141844ec9b58a Mon Sep 17 00:00:00 2001 From: Joshua Potter Date: Sun, 3 Jul 2022 06:50:31 -0600 Subject: [PATCH] Fix up with anki libraries. --- .githooks/pre-commit | 13 ++++++++++ .gitignore | 41 +++++++++++++++++++++++++++++++ README.md | 38 ++++++++++++++++++++--------- parser.py => __init__.py | 52 ++++++++++++++++++++++++++++++---------- config.json | 1 + 5 files changed, 122 insertions(+), 23 deletions(-) create mode 100755 .githooks/pre-commit create mode 100644 .gitignore rename parser.py => __init__.py (75%) create mode 100644 config.json diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..c0fb359 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e + +filesToFormat=$( + git --no-pager diff --name-status --no-color --cached | \ + awk '$1 != "D" && $2 ~ /\.py/ {print $NF}' +) + +for path in $filesToFormat +do + black --quiet $path + git add $path +done diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ab2f8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json diff --git a/README.md b/README.md index 5eb7759..a38cfa8 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,16 @@ different options for parts of prompts. This was designed to handle synonyms in a clean way. Consider a [total order](https://en.wikipedia.org/wiki/Total_order). What this -is does not matter; what it could also be called does. What some people call a +is does not matter; other names it may have does. What some people call a "total order", others call a "linear order". Though this example is simple, it -does highlight an issue - remembering the various synonyms used to describe -a concept is important for fluency. +highlights an important issue - remembering the various synonyms used to +describe a concept is necessary for fluency. -As of now, to handle this situation, it is probably best to use two flashcards, -one with prompt "Total Order" and another with prompt "Linear Order". In some -cases though, it'd be nice if the flashcard could *choose* which term it shows -when it shows it. That is, it'd be nice to have a single card and allow Anki to -randomly choose to show "Total Order" *or* "Linear Order". +As of now, to handle this situation, it is probably suggested to use two +flashcards, one with prompt "Total Order" and another with prompt "Linear Order". +In some cases though, it'd be nice if the flashcard could *choose* which term it +shows when it shows it. That is, it'd be nice to have a single card and allow +Anki to randomly choose to show "Total Order" *or* "Linear Order". To do so, we can install this plugin and write the following: @@ -27,10 +27,26 @@ Here, `'(` is used to indicate the start of a set of choices Anki can display, end of the set. The result is either "Total Order" or "Linear Order" at time of prompting. +You can also nest choices if need be: + +``` +'('(Logical|Valid) Consequence|Entailment) +``` + +will yield either "Logical Consequence", "Valid Consequence", or "Entailment". + ## Configuration -TODO +From "Tools > Add-ons", select the `anki-synonyms` entry and select "Config" +to reveal a dialog with contents: -## Nesting +```json +{ + "CHOICE_TAG": "|", + "END_TAG": ")", + "START_TAG": "'(" +} +``` -TODO +Update these accordingly if the default `'(|)` set of operators do not mesh with +the text in your questions and answers. diff --git a/parser.py b/__init__.py similarity index 75% rename from parser.py rename to __init__.py index 843762e..315fa44 100644 --- a/parser.py +++ b/__init__.py @@ -1,14 +1,26 @@ -from dataclasses import dataclass -from typing import Optional, Union - import copy import enum import random +from dataclasses import dataclass +from typing import Optional, Union -START_TAG = "'(" -END_TAG = ")" -CHOICE_TAG = "|" +from anki import hooks +from anki.template import TemplateRenderContext, TemplateRenderOutput +from aqt import mw + + +config = mw.addonManager.getConfig(__name__) + + +START_TAG: str = config["START_TAG"] +END_TAG: str = config["END_TAG"] +CHOICE_TAG: str = config["CHOICE_TAG"] + + +assert ( + len(set([START_TAG, END_TAG, CHOICE_TAG])) == 3 +), "Must have unique start, end, and choice operators." class Tag(enum.Enum): @@ -106,16 +118,32 @@ def run_parser(arg: str) -> str: respectively, parsing "'(hello|world)" yields either "hello" or "world". """ tokens = _tokenize(arg) - stack: list[list[str]] = [[]] + buffer: list[str] = [""] + stack: list[list[str]] = [] for token in tokens: if token is Tag.START: + buffer.append("") stack.append([]) elif token is Tag.END: + stack[-1].append(buffer.pop()) ts = stack.pop() - stack[-1].append(random.choice(ts)) + buffer[-1] += random.choice(ts) elif token is Tag.CHOICE: - pass + stack[-1].append(buffer[-1]) + buffer[-1] = "" else: - stack[-1].append(token) - assert len(stack) == 1, "Stack is larger than a single element" - return "".join(stack[0]) + buffer[-1] += token + assert not stack, "Stack should be empty" + assert len(buffer) == 1, "Buffer should only have one element." + return buffer[0] + + +def on_card_render( + output: TemplateRenderOutput, + _unused_context: TemplateRenderContext, +): + output.question_text = run_parser(output.question_text) + output.answer_text = run_parser(output.answer_text) + + +hooks.card_did_render.append(on_card_render) diff --git a/config.json b/config.json new file mode 100644 index 0000000..88f66bc --- /dev/null +++ b/config.json @@ -0,0 +1 @@ +{"START_TAG": "'(", "END_TAG": ")", "CHOICE_TAG": "|"}