-
Notifications
You must be signed in to change notification settings - Fork 707
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* reading csv * refactored problem to it's own module * new quiz test * checks answer * started tests for asking questions * running quiz * supporting flags * able to set timer * start timer * fully running quiz * renamed quiz to myquiz
- Loading branch information
1 parent
dae5550
commit 5ea0850
Showing
8 changed files
with
491 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/csv" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"time" | ||
|
||
quiz "github.com/gophercises/quiz/students/hackeryarn/myquiz" | ||
"github.com/gophercises/quiz/students/hackeryarn/problem" | ||
) | ||
|
||
const ( | ||
// FileFlag is used to set a file for the questions | ||
FileFlag = "file" | ||
// FileFlagValue is the value used when no FileFlag is provided | ||
FileFlagValue = "problems.csv" | ||
// FileFlagUsage is the help string for the FileFlag | ||
FileFlagUsage = "Questions file" | ||
|
||
// TimerFlag is used for setting a timer for the quiz | ||
TimerFlag = "timer" | ||
// TimerFlagValue is the value used when no TimerFlag is provided | ||
TimerFlagValue = 30 | ||
// TimerFlagUsage is the help string for the TimerFlag | ||
TimerFlagUsage = "Amount of seconds the quiz will allow" | ||
) | ||
|
||
// Flagger configures the flags used | ||
type Flagger interface { | ||
StringVar(p *string, name, value, usage string) | ||
IntVar(p *int, name string, value int, usage string) | ||
} | ||
|
||
type quizFlagger struct{} | ||
|
||
func (q *quizFlagger) StringVar(p *string, name, value, usage string) { | ||
flag.StringVar(p, name, value, usage) | ||
} | ||
|
||
func (q *quizFlagger) IntVar(p *int, name string, value int, usage string) { | ||
flag.IntVar(p, name, value, usage) | ||
} | ||
|
||
// Timer is used to start a timer | ||
type Timer interface { | ||
NewTimer(d time.Duration) *time.Timer | ||
} | ||
|
||
type quizTimer struct{} | ||
|
||
func (q quizTimer) NewTimer(d time.Duration) *time.Timer { | ||
return time.NewTimer(d) | ||
} | ||
|
||
// ReadCSV parses the CSV file into a Problem struct | ||
func ReadCSV(reader io.Reader) quiz.Quiz { | ||
csvReader := csv.NewReader(reader) | ||
|
||
problems := []problem.Problem{} | ||
for { | ||
record, err := csvReader.Read() | ||
if err == io.EOF { | ||
break | ||
} else if err != nil { | ||
log.Fatalln("Error reading CSV:", err) | ||
} | ||
|
||
problems = append(problems, problem.New(record)) | ||
} | ||
|
||
return quiz.New(problems) | ||
} | ||
|
||
// TimerSeconds is the amount of time allowed for the quiz | ||
var TimerSeconds int | ||
var file string | ||
|
||
// ConfigFlags sets all the flags used by the application | ||
func ConfigFlags(f Flagger) { | ||
f.StringVar(&file, FileFlag, FileFlagValue, FileFlagUsage) | ||
f.IntVar(&TimerSeconds, TimerFlag, TimerFlagValue, TimerFlagUsage) | ||
} | ||
|
||
// StartTimer begins a timer once the user provides input | ||
func StartTimer(w io.Writer, r io.Reader, timer Timer) *time.Timer { | ||
fmt.Fprint(w, "Ready to start?") | ||
fmt.Fscanln(r) | ||
|
||
return timer.NewTimer(time.Second * time.Duration(TimerSeconds)) | ||
} | ||
|
||
func init() { | ||
flagger := &quizFlagger{} | ||
ConfigFlags(flagger) | ||
|
||
flag.Parse() | ||
} | ||
|
||
func main() { | ||
file, err := os.Open(file) | ||
if err != nil { | ||
log.Fatalln("Could not open file", err) | ||
} | ||
|
||
quiz := ReadCSV(file) | ||
|
||
timer := StartTimer(os.Stdout, os.Stdin, quizTimer{}) | ||
go func() { | ||
<-timer.C | ||
fmt.Println("") | ||
quiz.PrintResults(os.Stdout) | ||
os.Exit(0) | ||
}() | ||
|
||
quiz.Run(os.Stdout, os.Stdin) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package quiz | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
|
||
"github.com/gophercises/quiz/students/hackeryarn/problem" | ||
) | ||
|
||
// Quiz represents the quiz to be given to the user | ||
type Quiz struct { | ||
problems []problem.Problem | ||
rightAnswers int | ||
} | ||
|
||
// Run runs the quiz for all the problems keeping track of correct answers | ||
func (q *Quiz) Run(w io.Writer, r io.Reader) { | ||
for _, problem := range q.problems { | ||
problem.AskQuestion(w) | ||
correct := problem.CheckAnswer(r) | ||
if correct { | ||
q.rightAnswers++ | ||
} | ||
} | ||
|
||
q.PrintResults(w) | ||
} | ||
|
||
// PrintResults outputs the results of the quiz | ||
func (q Quiz) PrintResults(w io.Writer) { | ||
fmt.Fprintf(w, "You got %d questions right!\n", q.rightAnswers) | ||
} | ||
|
||
// New creates a new quiz from the supplied slice of problems | ||
func New(problems []problem.Problem) Quiz { | ||
return Quiz{ | ||
problems: problems, | ||
rightAnswers: 0, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package quiz | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"reflect" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/gophercises/quiz/students/hackeryarn/problem" | ||
) | ||
|
||
func TestNew(t *testing.T) { | ||
problems := sampleProblems() | ||
|
||
want := Quiz{problems: problems, rightAnswers: 0} | ||
got := New(problems) | ||
|
||
if !reflect.DeepEqual(want, got) { | ||
t.Errorf("expeted to create quiz %v got %v", want, got) | ||
} | ||
} | ||
|
||
func TestRun(t *testing.T) { | ||
t.Run("it runs the quiz", func(t *testing.T) { | ||
buffer := &bytes.Buffer{} | ||
quiz := createQuiz() | ||
runQuiz(buffer, &quiz) | ||
|
||
expectedResults := 2 | ||
results := quiz.rightAnswers | ||
|
||
if expectedResults != results { | ||
t.Errorf("expected right answers of %v, got %v", | ||
expectedResults, results) | ||
} | ||
|
||
expectedOutput := "7+3: 1+1: You got 2 questions right!\n" | ||
|
||
if buffer.String() != expectedOutput { | ||
t.Errorf("expected full output %v, got %v", | ||
expectedOutput, buffer) | ||
} | ||
|
||
}) | ||
} | ||
|
||
func sampleProblems() []problem.Problem { | ||
record1 := []string{"7+3", "10"} | ||
record2 := []string{"1+1", "2"} | ||
|
||
return []problem.Problem{ | ||
problem.New(record1), | ||
problem.New(record2), | ||
} | ||
} | ||
|
||
func createQuiz() Quiz { | ||
problems := sampleProblems() | ||
return New(problems) | ||
} | ||
|
||
func runQuiz(buffer io.Writer, quiz *Quiz) { | ||
answers := strings.NewReader("10\n2\n") | ||
quiz.Run(buffer, answers) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package problem | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"strings" | ||
) | ||
|
||
// Problem represents a single question answer pair | ||
type Problem struct { | ||
question string | ||
answer string | ||
} | ||
|
||
// CheckAnswer checks the answer against the provided input | ||
func (p Problem) CheckAnswer(r io.Reader) bool { | ||
answer := readAnswer(r) | ||
|
||
if answer != p.answer { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func readAnswer(r io.Reader) (answer string) { | ||
_, err := fmt.Fscanln(r, &answer) | ||
if err != nil { | ||
log.Fatalln("Error reading in answer", err) | ||
} | ||
|
||
return strings.TrimSpace(answer) | ||
} | ||
|
||
// AskQuestion prints out the question | ||
func (p Problem) AskQuestion(w io.Writer) { | ||
_, err := fmt.Fprintf(w, "%s: ", p.question) | ||
if err != nil { | ||
log.Fatalln("Could not ask the question", err) | ||
} | ||
} | ||
|
||
// New creates a Problem from a provided CSV record | ||
func New(record []string) Problem { | ||
return Problem{ | ||
question: record[0], | ||
answer: record[1], | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package problem | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
) | ||
|
||
func TestNew(t *testing.T) { | ||
record := []string{"question", "answer"} | ||
|
||
want := Problem{"question", "answer"} | ||
got := New(record) | ||
|
||
if got != want { | ||
t.Errorf("expected to create problem %v got %v", want, got) | ||
} | ||
} | ||
|
||
func TestCheckAnswer(t *testing.T) { | ||
problem := createProblem() | ||
|
||
t.Run("it checks the correct answer", func(t *testing.T) { | ||
answer := getAnswer(problem, "10\n") | ||
|
||
checkAnswer(t, answer, true) | ||
}) | ||
|
||
t.Run("it checks incorrect answer", func(t *testing.T) { | ||
answer := getAnswer(problem, "2\n") | ||
|
||
checkAnswer(t, answer, false) | ||
}) | ||
} | ||
|
||
func TestAskQuestion(t *testing.T) { | ||
problem := createProblem() | ||
|
||
t.Run("it asks the question", func(t *testing.T) { | ||
buffer := bytes.NewBuffer(nil) | ||
|
||
problem.AskQuestion(buffer) | ||
|
||
want := "7+3: " | ||
got := buffer.String() | ||
|
||
if want != got { | ||
t.Errorf("Expected question %s, got %s", want, got) | ||
} | ||
}) | ||
|
||
} | ||
|
||
func createProblem() Problem { | ||
record := []string{"7+3", "10"} | ||
return New(record) | ||
} | ||
|
||
func getAnswer(problem Problem, input string) bool { | ||
r := bytes.NewBufferString(input) | ||
|
||
return problem.CheckAnswer(r) | ||
} | ||
|
||
func checkAnswer(t *testing.T, got, want bool) { | ||
if want != got { | ||
t.Errorf("Expected to return %v got %v", want, got) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
5+5,10 | ||
1+1,2 | ||
8+3,11 | ||
1+2,3 | ||
8+6,14 | ||
3+1,4 | ||
1+4,5 | ||
5+1,6 | ||
2+3,5 | ||
3+3,6 | ||
2+4,6 | ||
5+2,7 |
Oops, something went wrong.