Commit e176c53
Changed files (4)
question.go
@@ -9,45 +9,61 @@ import (
"strings"
)
+// assignCategory figures out what category
+// this question is a member of
+func assignCategory(category string, record *Record) {
+ if _, inMap := recordMap[category]; inMap != true {
+ recordMap[category] = []Record{}
+ categoryKeys = append(categoryKeys, category)
+ record.Category = len(categoryKeys) - 1
+ } else {
+ for i, value := range categoryKeys {
+ if value == category {
+ record.Category = i
+ }
+ }
+ }
+}
+
// parseLine parses a csv line and returns a single record
-func parseLine(line string) (Record, error) {
+func parseLine(line string) error {
var record Record
if strings.HasPrefix(line, "#") {
- return record, nil
+ return nil
} else if strings.Compare(strings.TrimSpace(line), "") == 0 {
- return record, nil
+ return nil
}
tokens := strings.Split(line, ",")
if len(tokens) != 3 {
- return record, fmt.Errorf("Unable to parse line for %s", line)
+ return fmt.Errorf("Unable to parse line for %s", line)
}
record.Question = strings.TrimSpace(tokens[0])
record.Answer = strings.TrimSpace(tokens[1])
- record.Category = strings.TrimSpace(tokens[2])
- return record, nil
+ category := strings.TrimSpace(strings.ToLower(tokens[1]))
+ assignCategory(category, &record)
+ recordMap[category] = append(recordMap[category], record)
+ return nil
}
// loadFile reads the lines of the Answer directory and returns an array of Records
// The questions are expected in the form of a csv in the order
// question, Category, category
// Lines starting with '#' are ignored
-func LoadFile(file *os.File) ([]Record, error) {
+func LoadFile(file *os.File) error {
log.Printf("Loading file: %+v", file.Name())
var records []Record
scanner := bufio.NewScanner(file)
for scanner.Scan() {
- if record, err := parseLine(scanner.Text()); err != nil {
+ if err := parseLine(scanner.Text()); err != nil {
log.Printf("Error while parsing line: %+v. \nContinuing execution", err)
- } else if record.Question != "" {
- records = append(records, record)
}
}
log.Printf("Loaded %d questions from %s.", len(records), file.Name())
- return records, nil
+ return nil
}
// loadRecords loads the records into memory according to
-// the supplied configuration
+// the supplied configuration for the given file.
func loadRecords(path string, fileInfo os.FileInfo, err error) error {
file, err := os.Open(path)
if err != nil {
@@ -58,13 +74,11 @@ func loadRecords(path string, fileInfo os.FileInfo, err error) error {
if err != nil {
return err
}
- var loadedRecords []Record
if fileStats.Mode().IsRegular() {
- loadedRecords, err = LoadFile(file)
+ err = LoadFile(file)
if err != nil {
return err
}
- records = append(records, loadedRecords...)
}
return nil
}
server.go
@@ -42,11 +42,9 @@ QUESTIONS = "/path/to/questions"
USER_TESTS = "/path/to/tests"
`
+// the running configuration for the server
var serverConfig SERVER_CONFIG
-// Contains the questions from which to pull
-var records []Record
-
//loadServerConfiguration loads the configuration file
// cfgFile : the file that contains the configuration
func loadServerConfiguration(cfgFile string) SERVER_CONFIG {
@@ -95,7 +93,11 @@ func ExecuteServer() {
log.Printf("Failed to load questions due to error: %+v", err)
os.Exit(EXIT_CODE.FILE_IO_ERROR)
}
- log.Printf("Loaded %d questions from %s.", len(records), serverConfig.QUESTIONS)
+ numQuestions := 0
+ for _, recordArray := range recordMap {
+ numQuestions += len(recordArray)
+ }
+ log.Printf("Loaded %d questions from %s with %d categories.", numQuestions, serverConfig.QUESTIONS, len(categoryKeys))
rand.Seed(time.Now().Unix())
Listen(serverConfig)
}
serverHandlers.go
@@ -12,15 +12,25 @@ import (
"strconv"
)
+// buildRecordArray builds an array of Records based on the parameter criteria
+func buildRecordArray(numQuestions int) []Record {
+ var keyIndex, valueIndex int
+ var recordArray = []Record{}
+ for i := 0; i < numQuestions; i++ {
+ keyIndex = rand.Intn(len(categoryKeys))
+ valueIndex = rand.Intn(len(recordMap[categoryKeys[keyIndex]]))
+ recordArray = append(recordArray, recordMap[categoryKeys[keyIndex]][valueIndex])
+ }
+ return recordArray
+}
+
// handleRequestForTest provides a JSON formatted test for the client
func handleRequestForTest(writer http.ResponseWriter, request *http.Request) error {
var giveRecords []Record
var err error
questions := request.FormValue("questions")
numQuestions, err := strconv.Atoi(questions)
- for i := 0; i < numQuestions; i += 1 {
- giveRecords = append(giveRecords, records[rand.Intn(len(records))])
- }
+ giveRecords = buildRecordArray(numQuestions)
data, err := json.Marshal(giveRecords)
if err != nil {
return fmt.Errorf("Error building questions: %+v", err)
@@ -129,5 +139,4 @@ func init() {
API_ROOT + "/test": ServerHandler{
Request: API_ROOT + "/test",
HandleFunction: handleTestQueries}}
-
}
structures.go
@@ -3,6 +3,18 @@ package main
// API_ROOT defines the root path for the web api interface
const API_ROOT = "/api"
+// recordMap contains all records grouped into
+// categorical buckets. the category serves as
+// the keyname. the map is populated through the
+// loadRecords function call.
+var recordMap map[string][]Record
+
+// categoryKeys contains the list of categories
+// available from loading the questions.
+// is used for randomly selecting questions based
+// on category
+var categoryKeys []string
+
// EXIT_CODES define exit error codes
type EXIT_CODES struct {
BAD_CONFIG,
@@ -19,8 +31,8 @@ var EXIT_CODE = EXIT_CODES{
// Record stores information related to a single record
type Record struct {
Question,
- Answer,
- Category string
+ Answer string
+ Category,
ID int
}
@@ -37,3 +49,7 @@ type ClientTest struct {
Score float32
Username string
}
+
+func init() {
+ recordMap = map[string][]Record{}
+}