Commit f59e5c8

Richard Luby <richluby@gmail.com>
2016-12-08 08:21:45
added structures for heirarchical tags
tags stored separately from records. tags have references to parent/children indices for the category array. records hold index values to relevant tag(s)
1 parent d1b685c
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{}
 }