Standardize cat/puzzle command args

This commit is contained in:
Neale Pickett 2020-09-14 18:23:56 -06:00
parent 8ad42b2dc7
commit ccd9cea29d
7 changed files with 69 additions and 76 deletions

View File

@ -7,26 +7,12 @@ import random
import shutil
import sys
parser = argparse.ArgumentParser("Generate a puzzle")
parser.add_argument("--file", dest="file", help="File to provide, instead of puzzle")
parser.add_argument("--answer", dest="answer", help="Answer to check, instead of providing puzzle")
args = parser.parse_args()
seed = hash(os.getenv("SEED"))
random.seed(seed)
random.seed(os.getenv("SEED", ""))
words = ["apple", "pear", "peach", "tangerine", "orange", "potato", "carrot", "pea"]
answer = ' '.join(random.sample(words, 4))
if args.file:
f = open(args.file, "rb")
shutil.copyfileobj(f, sys.stdout.buffer)
elif args.answer:
if args.answer == answer:
print("correct")
else:
print("incorrect")
else:
def puzzle():
number = random.randint(20, 500)
obj = {
"Pre": {
@ -53,3 +39,22 @@ else:
}
}
json.dump(obj, sys.stdout)
def open_file(filename):
f = open(filename, "rb")
shutil.copyfileobj(f, sys.stdout.buffer)
def check_answer(check):
if answer == check:
print("correct")
else:
print("incorrect")
if len(sys.argv) == 1:
puzzle()
elif sys.argv[1] == "file":
open_file(sys.argv[2])
elif sys.argv[1] == "answer":
check_answer(sys.argv[2])
else:
raise RuntimeError("Unknown command: %s" % sys.argv[1])

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"log"
"os/exec"
"path"
"strconv"
"strings"
"time"
@ -120,13 +121,19 @@ type FsCommandCategory struct {
timeout time.Duration
}
// Inventory returns a list of point values for this category.
func (c FsCommandCategory) Inventory() ([]int, error) {
func (c FsCommandCategory) run(command string, args ...string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
cmd := exec.CommandContext(ctx, c.command, "inventory")
stdout, err := cmd.Output()
cmdargs := append([]string{command}, args...)
cmd := exec.CommandContext(ctx, "./"+path.Base(c.command), cmdargs...)
cmd.Dir = path.Dir(c.command)
return cmd.Output()
}
// Inventory returns a list of point values for this category.
func (c FsCommandCategory) Inventory() ([]int, error) {
stdout, err := c.run("inventory")
if err != nil {
return nil, err
}
@ -143,11 +150,7 @@ func (c FsCommandCategory) Inventory() ([]int, error) {
func (c FsCommandCategory) Puzzle(points int) (Puzzle, error) {
var p Puzzle
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
cmd := exec.CommandContext(ctx, c.command, "puzzle", strconv.Itoa(points))
stdout, err := cmd.Output()
stdout, err := c.run("puzzle", strconv.Itoa(points))
if err != nil {
return p, err
}
@ -163,21 +166,13 @@ func (c FsCommandCategory) Puzzle(points int) (Puzzle, error) {
// Open returns an io.ReadCloser for the given filename.
func (c FsCommandCategory) Open(points int, filename string) (ReadSeekCloser, error) {
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
cmd := exec.CommandContext(ctx, c.command, "file", strconv.Itoa(points), filename)
stdout, err := cmd.Output()
stdout, err := c.run("file", strconv.Itoa(points), filename)
return nopCloser{bytes.NewReader(stdout)}, err
}
// Answer checks whether an answer is correct.
func (c FsCommandCategory) Answer(points int, answer string) bool {
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
cmd := exec.CommandContext(ctx, c.command, "answer", strconv.Itoa(points), answer)
stdout, err := cmd.Output()
stdout, err := c.run("answer", strconv.Itoa(points), answer)
if err != nil {
log.Printf("ERROR: Answering %d points: %s", points, err)
return false

View File

@ -3,6 +3,7 @@ package transpile
import (
"log"
"sort"
"strings"
"github.com/spf13/afero"
)
@ -20,6 +21,9 @@ func FsInventory(fs afero.Fs) (Inventory, error) {
inv := make(Inventory)
for _, ent := range dirEnts {
if strings.HasPrefix(ent.Name(), ".") {
continue
}
if ent.IsDir() {
name := ent.Name()
c := NewFsCategory(fs, name)

View File

@ -338,18 +338,19 @@ type FsCommandPuzzle struct {
timeout time.Duration
}
func (fp FsCommandPuzzle) run(args ...string) ([]byte, error) {
func (fp FsCommandPuzzle) run(command string, args ...string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), fp.timeout)
defer cancel()
cmd := exec.CommandContext(ctx, "./"+path.Base(fp.command), args...)
cmdargs := append([]string{command}, args...)
cmd := exec.CommandContext(ctx, "./"+path.Base(fp.command), cmdargs...)
cmd.Dir = path.Dir(fp.command)
return cmd.Output()
}
// Puzzle returns a Puzzle struct for the current puzzle.
func (fp FsCommandPuzzle) Puzzle() (Puzzle, error) {
stdout, err := fp.run()
stdout, err := fp.run("puzzle")
if exiterr, ok := err.(*exec.ExitError); ok {
return Puzzle{}, errors.New(string(exiterr.Stderr))
} else if err != nil {
@ -379,7 +380,7 @@ func (c nopCloser) Close() error {
// Open returns a newly-opened file.
// BUG(neale): FsCommandPuzzle.Open() reads everything into memory, and will suck for large files.
func (fp FsCommandPuzzle) Open(filename string) (ReadSeekCloser, error) {
stdout, err := fp.run("--file", filename)
stdout, err := fp.run("file", filename)
buf := nopCloser{bytes.NewReader(stdout)}
if err != nil {
return buf, err
@ -390,7 +391,7 @@ func (fp FsCommandPuzzle) Open(filename string) (ReadSeekCloser, error) {
// Answer checks whether the given answer is correct.
func (fp FsCommandPuzzle) Answer(answer string) bool {
stdout, err := fp.run("--answer", answer)
stdout, err := fp.run("answer", answer)
if err != nil {
log.Printf("ERROR: checking answer: %s", err)
return false

View File

@ -21,8 +21,8 @@ case $1:$2:$3 in
}
EOT
;;
puzzle:*:)
fail "No such puzzle"
puzzle:*)
fail "No such puzzle: $2"
;;
file:1:moo.txt)
echo "Moo."

View File

@ -1,7 +1,12 @@
#! /bin/sh
case $1 in
"")
fail () {
echo "ERROR: $*" 1>&2
exit 1
}
case $1:$2 in
puzzle:)
cat <<'EOT'
{
"Answers": ["answer"],
@ -12,34 +17,23 @@ case $1 in
}
EOT
;;
-file|--file)
case $2 in
moo.txt)
file:moo.txt)
echo "Moo."
;;
*)
echo "ERROR: no such file: $1" 1>&2
exit 1
file:*)
fail "no such file: $1"
;;
esac
;;
-answer|--answer)
case $2 in
moo)
answer:moo)
echo "correct"
;;
error)
echo "error" 1>&2
exit 1
answer:error)
fail "you requested an error"
;;
*)
answer:*)
echo "incorrect"
;;
esac
;;
*)
echo "ERROR: don't know what to do with $1" 1>&2
exit 1
fail "What is $1"
;;
esac

View File

@ -88,11 +88,6 @@ function submit(e) {
e.preventDefault()
let data = new FormData(e.target)
// Kludge for patterned answers
let xAnswer = data.get("xAnswer")
if (xAnswer) {
data.set("answer", xAnswer)
}
window.data = data
fetch("answer", {
method: "POST",
@ -193,7 +188,6 @@ function answerCheck(e) {
checkAnswer(answer)
.then (correct => {
document.querySelector("[name=xAnswer").value = correct || answer
if (correct) {
ok.textContent = "⭕"
ok.title = "Possibly correct"