Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions tests/python/test_findings.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,68 @@ def test_the_commit_is_checked_in_the_ledgers_own_repository(paths: tuple[Path,
)
run(ledger, "add", "A.", "--commit", own, "--topic=x", "--ola", AGREED)
assert [e["commit"] for e in entries(ledger)] == [own]


LINE = "Two delays in series slow the response twice over."


def asked(paths: tuple[Path, Path], text: str, words: str | None) -> str:
"""Store a question in the store beside the search record, answered with `words`."""
record = {"id": "q7", "kind": "question", "text": text, "default": "keep it",
"choices": [], "item": None, "source": "main", "state": "open", "words": None,
"at": STAMP} # fmt: skip
store = paths[1].with_name("questions.jsonl")
lines = [record] + ([{**record, "state": "answered", "words": words}] if words else [])
store.parent.mkdir(parents=True, exist_ok=True)
store.write_text("".join(json.dumps(r) + "\n" for r in lines))
return "q7"


def by_question(paths: tuple[Path, Path], qid: str, text: str = LINE) -> list[str]:
return run(paths, "add", text, "--commit", HEAD, "--topic=delay", "--question", qid)


def test_an_answered_agreeing_question_lets_the_line_in(paths: tuple[Path, Path]) -> None:
qid = asked(paths, f'May the ledger say: "{LINE}"?', "Yes")
by_question(paths, qid)
[entry] = entries(paths)
assert entry["text"] == LINE and "q7" in str(entry["ola"]) and "Yes" in str(entry["ola"])


def test_an_unanswered_question_is_refused(paths: tuple[Path, Path]) -> None:
qid = asked(paths, f'May the ledger say: "{LINE}"?', None)
with pytest.raises(findings.RefusedError, match="not answered"):
by_question(paths, qid)
assert not paths[0].exists()


@pytest.mark.parametrize("words", ["Change it", "No", "Yes, but say three delays"])
def test_an_answer_other_than_agreement_is_refused(paths: tuple[Path, Path], words: str) -> None:
qid = asked(paths, f'May the ledger say: "{LINE}"?', words)
with pytest.raises(findings.RefusedError, match="agree"):
by_question(paths, qid)
assert not paths[0].exists()


def test_a_line_the_question_does_not_hold_is_refused(paths: tuple[Path, Path]) -> None:
qid = asked(paths, f'May the ledger say: "{LINE}"?', "Yes")
with pytest.raises(findings.RefusedError, match="does not hold"):
by_question(paths, qid, "Two delays in series slow the response.")
assert not paths[0].exists()


def test_an_unknown_question_is_refused(paths: tuple[Path, Path]) -> None:
with pytest.raises(findings.RefusedError, match="no question"):
by_question(paths, "q9")


def test_replace_takes_an_answered_question_too(paths: tuple[Path, Path]) -> None:
add(paths, "First result.", "a")
qid = asked(paths, f'May the ledger say: "{LINE}"?', "Agreed.")
run(paths, "replace", "1", LINE, "--question", qid)
assert [e["text"] for e in entries(paths)] == [LINE]


def test_ola_and_question_together_are_refused(paths: tuple[Path, Path]) -> None:
with pytest.raises(SystemExit):
run(paths, "add", LINE, "--commit", HEAD, "--topic=x", "--ola", AGREED, "--question", "q7")
38 changes: 31 additions & 7 deletions tools/findings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
JSON entry per fact; `--commit` names a commit of the repository holding the
ledger, which may be private. A missing file is an empty ledger, and git
history keeps what `replace` and `drop` remove.
`add` and `replace` need `--ola`: Ola's agreement to the wording, found in this
session's transcript as `brief.py --ola` finds it. Each `search` appends its time and
`add` and `replace` need Ola's agreement to the wording: `--ola`, words found in
this session's transcript as `brief.py --ola` finds them, or `--question <id>`, a
question `questions.py` stored whose text holds the line and whose answer is
one of `AGREE`. Each `search` appends its time and
words to `<git-common-dir>/findings-searches.jsonl`, untracked; `questions.py
add` and design or probe briefs (`brief.py`) refuse through `unsearched` until
a search ran in the last hour. Standard library only.
Expand All @@ -37,6 +39,8 @@
#: Chosen: one line of a result, as questions.py caps a question at two.
LIMIT = 160
WINDOW = timedelta(hours=1)
#: Chosen: whole answers that agree; anything else, "Yes, but ..." too, is refused.
AGREE = {"yes", "agree", "agreed", "ok", "approve", "approved"}


class RefusedError(Exception):
Expand Down Expand Up @@ -70,14 +74,32 @@ def _line(n: int, e: dict[str, Any]) -> str:
return f"{n}. {e['text']} (commit {e['commit'][:8]}, {e['date']}; {', '.join(e['topics'])})"


def _worded(args: argparse.Namespace) -> str:
def _answered(args: argparse.Namespace, store: Path) -> str:
"""Ola's agreeing answer to question `args.question`, whose text holds the line."""
import questions

qid = f"q{args.question}" if args.question.isdigit() else args.question
record = next((r for r in questions.load(store)[0] if r["id"] == qid), None)
if record is None:
raise RefusedError(f"there is no question {qid}")
if record["state"] != "answered":
raise RefusedError(f"question {qid} is not answered")
if record["words"].strip(" .!").casefold() not in AGREE:
raise RefusedError(f'Ola did not agree to question {qid}: "{record["words"]}"')
if " ".join(args.text.split()) not in " ".join(record["text"].split()):
raise RefusedError(f"question {qid} does not hold this line")
return f'"{record["words"]}" (question {qid}, {record["at"]})'


def _worded(args: argparse.Namespace, searches: Path) -> str:
"""Ola's agreement with the wording, as brief.py quotes it, once the wording is plain."""
import brief # here, not at the top: these import this module
import questions

try:
questions._plain(args.text, set(args.term or ()), False)
quoted = brief._ola([args.ola])[1].strip()
quoted = _answered(args, searches.with_name("questions.jsonl")) if args.question \
else brief._ola([args.ola])[1].strip() # fmt: skip
except (questions.RefusedError, brief.BriefError) as error:
raise RefusedError(str(error)) from error
if len(args.text) > LIMIT:
Expand All @@ -91,7 +113,7 @@ def command(args: argparse.Namespace, ledger: Path, searches: Path, now: datetim
if args.cmd in ("replace", "drop") and not 0 < args.n <= len(entries):
raise RefusedError(f"the ledger has no entry {args.n}")
if args.cmd == "add":
ola = _worded(args)
ola = _worded(args, searches)
home = next(d for d in ledger.parents if d.is_dir()) # the ledger's own repository
shown = subprocess.run(["git", "-C", str(home), "show", "-s", "--format=%H %cs",
f"{args.commit}^{{commit}}"], capture_output=True, text=True,
Expand All @@ -101,7 +123,7 @@ def command(args: argparse.Namespace, ledger: Path, searches: Path, now: datetim
entries.append({"text": args.text, "commit": shown[0], "date": shown[1],
"topics": args.topic, "ola": ola}) # fmt: skip
elif args.cmd == "replace":
entries[args.n - 1].update(text=args.text, ola=_worded(args))
entries[args.n - 1].update(text=args.text, ola=_worded(args, searches))
elif args.cmd == "drop":
del entries[args.n - 1]
if args.cmd in ("add", "replace", "drop"):
Expand Down Expand Up @@ -130,7 +152,9 @@ def parser() -> argparse.ArgumentParser:
r.add_argument("n", type=int)
for s in (a, r):
s.add_argument("text")
s.add_argument("--ola", required=True, help="Ola's words agreeing to the wording")
agreed = s.add_mutually_exclusive_group(required=True)
agreed.add_argument("--ola", help="Ola's words agreeing to the wording")
agreed.add_argument("--question", help="an answered question holding the line, as q7")
s.add_argument("--term", action="append", help="a proper name read as an abbreviation")
a.add_argument("--commit", required=True, help="the commit that settled it")
a.add_argument("--topic", action="append", required=True, help="a word to find it by")
Expand Down
Loading