Commit f59e5c8
Changed files (5)
command.go
@@ -234,7 +234,7 @@ func runTest(clientTest *ClientTest) {
displayStringToMainScreen("\n")
for i, record := range clientTest.Records {
displayStringToMainScreen(fmt.Sprintf("%d) [%s] %s\n", i+1,
- COLOR_BLUE+categoryKeys[record.Category]+COLOR_RESET,
+ COLOR_BLUE+categoricalData.CategoryPath[record.Category]+COLOR_RESET,
record.Question))
updateMainView()
readUserInputSemaphore.P() // released when user presses 'Enter'
@@ -309,8 +309,8 @@ func getCategoriesFromServer(command Command) error {
if err != nil {
return err
}
- err = json.Unmarshal(data, &categoryKeys)
- log.Printf("Pulled %d categories from server.", len(categoryKeys))
+ err = json.Unmarshal(data, &categoricalData.CategoryPath)
+ log.Printf("Pulled %d categories from server.", len(categoricalData.CategoryPath))
return nil
}
@@ -340,10 +340,10 @@ func buildBluePrint(useBlueprint bool) (string, error) {
}
var blueprint string
displayStringToMainScreen("\nCategories\n----------\n")
- for i := 0; i < len(categoryKeys)-1; i += 2 {
+ for i := 0; i < len(categoricalData.CategoryPath)-1; i += 2 {
displayStringToMainScreen(fmt.Sprintf("%d) %-15s\t%d) %-15s\n",
- i, categoryKeys[i],
- i+1, categoryKeys[i+1]))
+ i, categoricalData.CategoryPath[i],
+ i+1, categoricalData.CategoryPath[i+1]))
}
displayStringToMainScreen("Enter the category numbers, separated by a comma: ")
updateMainView()
question.go
@@ -14,10 +14,10 @@ import (
func assignCategory(category string, record *Record) {
if _, inMap := recordMap[category]; inMap != true {
recordMap[category] = []Record{}
- categoryKeys = append(categoryKeys, category)
- record.Category = len(categoryKeys) - 1
+ categoricalData.CategoryPath = append(categoricalData.CategoryPath, category)
+ record.Category = len(categoricalData.CategoryPath) - 1
} else {
- for i, value := range categoryKeys {
+ for i, value := range categoricalData.CategoryPath {
if value == category {
record.Category = i
}
server.go
@@ -109,7 +109,7 @@ func ExecuteServer() {
for _, recordArray := range recordMap {
numQuestions += len(recordArray)
}
- log.Printf("Loaded %d questions from %s with %d categories.", numQuestions, serverConfig.QUESTIONS, len(categoryKeys))
+ log.Printf("Loaded %d questions from %s with %d categories.", numQuestions, serverConfig.QUESTIONS, len(categoricalData.CategoryPath))
rand.Seed(time.Now().Unix())
Listen(serverConfig)
}
serverHandlers.go
@@ -48,14 +48,14 @@ func buildRecordArray(numQuestions int, categories []int) []Record {
var recordArray = []Record{}
for i := 0; i < numQuestions; i++ {
if categories == nil {
- keyIndex = rand.Intn(len(categoryKeys))
+ keyIndex = rand.Intn(len(categoricalData.CategoryPath))
} else {
keyIndex = categories[rand.Intn(len(categories))]
- if keyIndex >= len(categoryKeys) {
+ if keyIndex >= len(categoricalData.CategoryPath) {
return nil
}
}
- recordArray = append(recordArray, getRecordForCategory(categoryKeys[keyIndex]))
+ recordArray = append(recordArray, getRecordForCategory(categoricalData.CategoryPath[keyIndex]))
}
return recordArray
}
@@ -177,7 +177,7 @@ func handleTestQueries(writer http.ResponseWriter, request *http.Request) {
// handleRequestForCategories returns the JSON-encoded array
// of categories available on the server
func handleRequestForCategories(writer http.ResponseWriter, request *http.Request) error {
- data, err := json.Marshal(categoryKeys)
+ data, err := json.Marshal(categoricalData.CategoryPath)
if err != nil {
return err
}
structures.go
@@ -19,11 +19,23 @@ var usedRecordMap map[string][]Record
// mapLock locks the maps to prevent concurrency issues
var mapLock *sync.Mutex
-// categoryKeys contains the list of categories
-// available from loading the questions.
-// is used for randomly selecting questions based
-// on category
-var categoryKeys []string
+// stores information for all categories
+var (
+ categoricalData CategoricalData
+)
+
+const CATEGORY_SEPARATOR = ":"
+
+type CategoricalData struct {
+ // categoryPaths stores the path of a category from the
+ // categories array. These indices must match exactly.
+ // Implmentation based on closure table from
+ // http://www.slideshare.net/billkarwin/models-for-hierarchical-data
+ // The path is stored using CATEGORY_SEPARATOR as the delimiter
+ // in the string.
+ CategoryPath []string
+ Category []Category
+}
// define colors for printing to terminals
const (
@@ -81,6 +93,30 @@ type CountingSemaphore struct {
// SetCapacity (int)
}
+// Category stores information related to categories
+// Storing pointers to the category array simplifies
+// sending the structures over the wire.
+type Category struct {
+ Children []int
+ // stores the indices to the array in
+ // 'categoricalData' of the children. Similar to a pointer,
+ // but can be sent across the network.
+
+ Parent, // the index in the array of 'categoricalData'
+ PathIndex int // index to categoryPath array
+ Value string // leaf portion of path
+}
+
+// returns the fully qualified path of this
+// category, or the empty string if the PathIndex
+// variable is too large
+func (cat Category) String() string {
+ if len(categoricalData.CategoryPath) < cat.PathIndex {
+ return ""
+ }
+ return categoricalData.CategoryPath[cat.PathIndex]
+}
+
// P incrememnts the counter here
// note: race conditions can totally happen
func (cSem *CountingSemaphore) P() {
@@ -119,5 +155,6 @@ func (cSem *CountingSemaphore) SetCapacity(cap int) {
func init() {
recordMap = map[string][]Record{}
usedRecordMap = map[string][]Record{}
+ categoricalData = CategoricalData{}
mapLock = &sync.Mutex{}
}