Commit 9eff758

Richard Luby <richluby@gmail.com>
2016-10-28 10:42:12
client posts results of test
client now posts test results and answers to server. client uses 'anonymous' if no user name is set
1 parent cf77ed1
client.go
@@ -2,13 +2,10 @@ package main
 
 import (
 	"bufio"
-	"encoding/json"
 	"flag"
 	"fmt"
 	"github.com/BurntSushi/toml"
-	"io/ioutil"
 	"log"
-	"net/http"
 	"os"
 	"strconv"
 	"strings"
@@ -20,6 +17,8 @@ type CLIENT_CONFIG struct {
 	SERVER string
 	// PORT is the port on which to connect
 	PORT int
+	// SERVER_URL contains the url to access the server, including the protocol
+	SERVER_URL string
 	// USE_HTTPS determines if the client should connect using HTTPS
 	USE_HTTPS bool
 	// USER defines the user that is in control of this session
@@ -32,35 +31,17 @@ var clientConfig CLIENT_CONFIG
 func loadClientConfiguration(cfgFile string) CLIENT_CONFIG {
 	log.Printf("Loading configuration from %s.", cfgFile)
 	_, err := toml.DecodeFile(cfgFile, &clientConfig)
-	if err != nil {
-		log.Fatalf("Could not read configuration file: %+v\n", err.Error())
-		os.Exit(EXIT_CODE.FILE_IO_ERROR)
-	}
-	return clientConfig
-}
-
-// getRecordFromServer retrieves a record from the server
-func getRecordFromServer(client *http.Client, config CLIENT_CONFIG, numQuestions int) ([]Record, error) {
-	var serverURL string
-	// allows adding configuration for port specific (ie HTTPS) requests
-	switch config.PORT {
+	switch clientConfig.PORT {
 	case 0:
-		serverURL = config.SERVER
+		clientConfig.SERVER_URL = clientConfig.SERVER
 	default:
-		serverURL = "http://" + config.SERVER + ":" + strconv.Itoa(config.PORT)
-	}
-	resp, err := client.Get(serverURL + API_ROOT + "/test" + "?questions=" + strconv.Itoa(numQuestions))
-	if err != nil {
-		return nil, err
+		clientConfig.SERVER_URL = "http://" + clientConfig.SERVER + ":" + strconv.Itoa(clientConfig.PORT)
 	}
-	defer resp.Body.Close()
-	var recordArray []Record
-	data, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
-		return nil, err
+		log.Fatalf("Could not read configuration file: %+v\n", err.Error())
+		os.Exit(EXIT_CODE.FILE_IO_ERROR)
 	}
-	err = json.Unmarshal(data, &recordArray)
-	return recordArray, err
+	return clientConfig
 }
 
 // initUserSession starts the interactive prompt for the user
@@ -72,6 +53,10 @@ func initUserSession() {
 	var err error
 	reader := bufio.NewReader(os.Stdin)
 	for {
+		if clientConfig.USER == "" {
+			fmt.Print("\nUsername is blank. Setting to 'anonymous.' Use 'user' to change user name.")
+			clientConfig.USER = "anonymous"
+		}
 		fmt.Printf("\n:> ")
 		input, _ = reader.ReadString('\n')
 		args := strings.Fields(input)
@@ -95,6 +80,12 @@ func ExecuteClient() {
 	clientConfig.SERVER = "127.0.0.1"
 	clientConfig.PORT = 80
 	clientConfig.USE_HTTPS = false
+	switch clientConfig.PORT {
+	case 0:
+		clientConfig.SERVER_URL = clientConfig.SERVER
+	default:
+		clientConfig.SERVER_URL = "http://" + clientConfig.SERVER + ":" + strconv.Itoa(clientConfig.PORT)
+	}
 	filePath := flag.String("file", "", "defines a path to the configuration file")
 	flag.Parse()
 	if strings.Compare(*filePath, "") != 0 {
command.go
@@ -2,7 +2,10 @@ package main
 
 import (
 	"bufio"
+	"bytes"
+	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"os"
 	"strconv"
@@ -82,12 +85,61 @@ func init() {
 	commandArray = append(commandArray, helpCommand)
 }
 
+// walks the user through the test questions
+// this function updates the records with the user responses
+func runTest(recordArray []ClientRecord) []ClientRecord {
+	reader := bufio.NewReader(os.Stdin)
+	var responseArray []ClientRecord
+	for i, record := range recordArray {
+		fmt.Printf("%d) %s\n", i+1, record.Question)
+		input, _ := reader.ReadString('\n')
+		input = strings.TrimSpace(input)
+		if strings.Compare(input, record.Answer) == 0 {
+			fmt.Println("Correct.")
+			recordArray[i].AnsweredCorrectly = true
+		} else if strings.Compare(input, "exit") == 0 {
+			return responseArray
+		} else {
+			fmt.Printf("Incorrect. Correct answer was: %s\n", record.Answer)
+			recordArray[i].ClientAnswer = input
+		}
+	}
+	return responseArray
+}
+
+// postRecordsToServer sends the client responses back to the server
+func postRecordsToServer(client *http.Client, recordArray *[]ClientRecord) error {
+	data, err := json.Marshal(recordArray)
+	if err != nil {
+		return err
+	}
+	_, err = client.Post(clientConfig.SERVER_URL+API_ROOT+"/test?username="+clientConfig.USER, "", bytes.NewBuffer(data))
+	return err
+}
+
+// getRecordFromServer retrieves a record from the server
+func getRecordFromServer(client *http.Client, config CLIENT_CONFIG, numQuestions int) ([]ClientRecord, error) {
+	// allows adding configuration for port specific (ie HTTPS) requests
+	resp, err := client.Get(clientConfig.SERVER_URL + API_ROOT + "/test" + "?questions=" + strconv.Itoa(numQuestions))
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	var recordArray []ClientRecord
+	data, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	err = json.Unmarshal(data, &recordArray)
+	return recordArray, err
+}
+
 // executeTest runs a user through a test
+// from retrieving the records to returning the answers
 func executeTest(args []string) error {
 	client := &http.Client{}
-	var recordArray []Record
+	var recordArray []ClientRecord
 	var err error
-	var input string
 	questions := 20
 	if len(args) > 0 {
 		questions, err = strconv.Atoi(args[0])
@@ -99,17 +151,6 @@ func executeTest(args []string) error {
 	if err != nil {
 		return fmt.Errorf("Error while request from server: %+v", err)
 	}
-	reader := bufio.NewReader(os.Stdin)
-	for i, record := range recordArray {
-		fmt.Printf("%d) %s\n", i+1, record.Question)
-		input, _ = reader.ReadString('\n')
-		if strings.Compare(strings.TrimSpace(input), record.Answer) == 0 {
-			fmt.Println("Correct.")
-		} else if strings.Compare(strings.TrimSpace(input), "exit") == 0 {
-			return nil
-		} else {
-			fmt.Printf("Incorrect. Correct answer was: %s\n", record.Answer)
-		}
-	}
-	return nil
+	runTest(recordArray)
+	return postRecordsToServer(client, &recordArray)
 }
serverHandlers.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"encoding/json"
+	"fmt"
 	"log"
 	"math/rand"
 	"net/http"
@@ -9,7 +10,7 @@ import (
 )
 
 // handleRequestForTest provides a JSON formatted test for the client
-func handleRequestForTest(writer http.ResponseWriter, request *http.Request) {
+func handleRequestForTest(writer http.ResponseWriter, request *http.Request) error {
 	var giveRecords []Record
 	var err error
 	questions := request.FormValue("questions")
@@ -19,10 +20,16 @@ func handleRequestForTest(writer http.ResponseWriter, request *http.Request) {
 	}
 	data, err := json.Marshal(giveRecords)
 	if err != nil {
-		log.Printf("Error building questions: %+v", err)
+		return fmt.Errorf("Error building questions: %+v", err)
 	}
 	writer.Write(data)
-	log.Printf("Gave %d questions to %s", numQuestions, request.RemoteAddr)
+	log.Printf("Gave %d questions to %s", len(giveRecords), request.RemoteAddr)
+	return nil
+}
+
+// handlePostingTest receives a client's test results, and then stores them
+func handlePostingTest(writer http.ResponseWriter, request *http.Request) error {
+	return nil
 }
 
 // handleTestRequests creates an array of records for the client
@@ -34,9 +41,15 @@ func handleTestQueries(writer http.ResponseWriter, request *http.Request) {
 	switch request.Method {
 	case "POST":
 		log.Printf("Client posting test: %+v\tvia %+v", request.RemoteAddr, request.UserAgent())
+		if err := handlePostingTest(writer, request); err != nil {
+			log.Printf("Error while receiving test: %+v", err)
+		}
 	case "GET":
 		log.Printf("Client requested test: %+v\tvia %+v", request.RemoteAddr, request.UserAgent())
-		handleRequestForTest(writer, request)
+		if err := handleRequestForTest(writer, request); err != nil {
+			log.Printf("Error while serving test: %+v", err)
+		}
+
 	}
 }
 
structures.go
@@ -23,3 +23,10 @@ type Record struct {
 	Category string
 	ID int
 }
+
+// ClientRecord stores the client response to a particular record
+type ClientRecord struct {
+	Record
+	ClientAnswer      string
+	AnsweredCorrectly bool
+}