Some changes big and small

Published by Karl on

Photo by CJ Dayrit on Unsplash

The initial scope of the project started out as a simple app to help my wife create tests for her class, but as she doesn’t have to books yet, she is thinking to re-mix the old tests into new ones once the books are available and the order of the characters is known. I could just let her do this with the XML files but its getting somewhat tricky to manage with the possible number of questions so this project is going to become a web app that she can use to remix the questions.

This should make things easier to manage and more interactive as well.

As such I’m going to halt development on this version and start to plan for a more “user friendly” version, probably web based.

I’m going to put the code for this version here and on my github account once its there.

package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "os"
    "text/template"
    "strconv"
    "bytes"
//    "golang.org/x/text/encoding/japanese"
)

//this is the template for the test
//(there could be multiple of these driven by a parameter
// in the test file)
const testPage = 
`
<link rel="stylesheet" href="kanji_test.css">

<table border="0" width="3000">
<tr>
<td class="question" style="vertical-align: top; width: 350px">{{.q9}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q8}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q7}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q6}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q5}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q4}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q3}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q2}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q1}}</td>
<td class="question" style="vertical-align: top; width: 350px">{{.q0}}</td>
<td><img src="images\header.png"/></td>
</tr></table>
`

const kanjiReadingTpl =
`
<table class="reading_part" width="280">
	<tr>
		<td align="center" style="vertical-align: middle;">
			<table class="reading_main" width="200">
				{{.question}}
			</table></td>
		<td style="vertical-align: middle;">
			<table class="reading_hints" width="80">
				{{.hints}}
			</table></td>
	</tr>
</table>
`

const kanjiWritingTpl =
`
<table class="writing_question" width="280">
	<tr>
		<td align="center">
			<table class="writng_main">
				<font size="20px"><img src="images\kanji-box.png" width="200"/></font>
			</table></td>
		<td>
			<table class="writing_hints">
				{{.hints}}
			</table></td>
	</tr>
</table>
`

const combinationWritingTpl =
`
<table class="combination_writing_question" width="280">
	<tr>
		<td align="center">
			<table class="writng_main">
				<tr><td><img src="images\top_brace.png" width="200"/></td></tr>
				<tr><td>{{.space}}</td></tr>
				<tr><td><img src="images\btm_brace.png" width="200"/></td></tr>
			</table></td>
		<td>
			<table class="writing_hints">
				{{.hints}}
			</table></td>
	</tr>
</table>
`


type Test struct {
	XMLName 	xml.Name	`xml:"test"`
	Questions 	[]Question	`xml:"question"`
}

type Question struct {
	XMLName		xml.Name	`xml:"question"`
	QType		string 		`xml:"type,attr"`
	Parts		[]Part		`xml:"part"`
}

type Part struct {
	XMLName		xml.Name	`xml:"part"`
	PrtType		string 		`xml:"type,attr"`
	Ans			int 		`xml:"answer,attr"`
	Content		string 		`xml:",innerxml"`
}



func main() {

	//register and setup the test template
	t:=template.Must(template.New("").Parse(testPage))
	

	//## TODO: use a parameter to drive which file to use
	// Open the test xml file
	xmlFile, err := os.Open("test1.xml")

	// if os.Open returns an error then handle it
	if err != nil {
		fmt.Println(err)
	}

	//fmt.Println("Successfully Opened xml file")

	// defer the closing of our xmlFile so that we can parse it later on
	defer xmlFile.Close()

	// read our opened xmlFile as a byte array.
	byteValue, _ := ioutil.ReadAll(xmlFile)

	// we initialize our test struct
	var test Test

	// we unmarshal the bytearray to the struct
	xml.Unmarshal(byteValue, &test)

    if err != nil {
        fmt.Printf("error: %q", err)
        return
    }

    //this is the map that will fill the template
    testQuestions := make(map[string]string)

    //itterate throught the questions and update the map for the template
	for i, q := range test.Questions{
		idx := "q" + strconv.Itoa(i)
		switch(q.QType){
		case "r":
			testQuestions[idx] = ReadingQuestion(idx, q)
		case "w":
			testQuestions[idx] = KanjiQuestion(idx, q)
		case "c":
			testQuestions[idx] = CombinationQuestion(idx, q)
		}
	}

	//generate the page using the template
 	t.Execute(os.Stdout,testQuestions)
}


//////////////////////////////////////////////////////////////// READING QUESTION
func ReadingQuestion(qNumber string, q Question) string {
	var qTable string
	KanjiReadingTemplate := template.Must(template.New("").Parse(kanjiReadingTpl))

	var tString bytes.Buffer
	qData := make(map[string]string)

	//fmt.Println(q)



	for _, p := range q.Parts{
		switch(p.PrtType){
		case "k":
			//fmt.Println("starting k - qtable=",qTable)
			if qData["question"] != "" { //we have a question open so dumpt it out
				//fmt.Println("dumping question ", qData["question"])
				KanjiReadingTemplate.Execute(&tString, qData)	
				//fmt.Println("tString after Dump: }\n\n\n", tString.String())
				qTable = qTable + tString.String()
				tString.Reset()
			}
			if qData["question"]=="" {
				//fmt.Println("starting with KANJI")
			}

			//start a new question
			qData = make(map[string]string)
			//fmt.Println("map cleared, now its ",qData," and qTable is ", qTable)
			qData = KanjiReading(p)
		case "h":

			//fmt.Println("starting h - qtable=",qTable)
			//question starts with hiragana, start a new template
			if (qTable=="") && (qData["question"]==""){ 			
				//fmt.Println("stating with HIRAGANA with ", p)	
				qData = Hiragana(p, make(map[string]string))
			} else {
				//fmt.Println("adding to qData ", qData)
				KanjiReadingTemplate.Execute(&tString, qData)	
				qData = Hiragana(p, make(map[string]string))
				//used to be: qData = Hiragana(p, qData)
			}
		}
	}

	//dump any open question to the string
	KanjiReadingTemplate.Execute(&tString, qData)	
	//fmt.Println("After Template String 5STRING}\n\n\n", tString.String())
	qTable = "<table width=\"350\"><tr><td align=\"center\"><img src=\"images\\" + qNumber + ".png\"/></td></tr></table>" + qTable + tString.String()

	return qTable
}


//////////////////////////////////////////////////////////////// KANJI QUESTION
func KanjiQuestion(qNumber string, q Question) string {
	var qTable string
	KanjiWritingTemplate := template.Must(template.New("").Parse(kanjiWritingTpl))
	KanjiReadingTemplate := template.Must(template.New("").Parse(kanjiReadingTpl))

	var tString bytes.Buffer
	qData := make(map[string]string)

	//fmt.Println(q)

	for _, p := range q.Parts{
		switch(p.PrtType){
		case "k":
			qData = KanjiWriting(p)
			KanjiWritingTemplate.Execute(&tString, qData)	
			qTable = qTable + tString.String()
			tString.Reset()
		case "h":
			//fmt.Println("starting h - qtable=",qTable)
			qData = Hiragana(p, make(map[string]string))
			KanjiReadingTemplate.Execute(&tString, qData)	
			qTable = qTable + tString.String()
			tString.Reset()
		}
	}

	return "<table width=\"350\"><tr><td align=\"center\"><img src=\"images\\" + qNumber + ".png\"/></td></tr></table>" + qTable
}

//////////////////////////////////////////////////////////////// KANJI QUESTION
func CombinationQuestion(qNumber string, q Question) string {
	var qTable string
	KanjiReadingTemplate := template.Must(template.New("").Parse(kanjiReadingTpl))
	CombinationTemplate := template.Must(template.New("").Parse(combinationWritingTpl))

	var tString bytes.Buffer
	qData := make(map[string]string)

	for _, p := range q.Parts{
		switch(p.PrtType){
		case "c":
			qData = CombinationWriting(p)
			CombinationTemplate.Execute(&tString, qData)	
			qTable = qTable + tString.String()
			tString.Reset()
		case "h":
			//fmt.Println("starting h - qtable=",qTable)
			qData = Hiragana(p, make(map[string]string))
			KanjiReadingTemplate.Execute(&tString, qData)	
			qTable = qTable + tString.String()
			tString.Reset()
		}
	}

	return "<table width=\"350\"><tr><td align=\"center\"><img src=\"images\\" + qNumber + ".png\"/></td></tr></table>" + qTable
}

//////////////////////////////////////////////////////////////// PART HANDLERS
func Hiragana(p Part, questionTemplateData map[string]string) map[string]string {
	for _, char := range p.Content {
		questionTemplateData["question"] = questionTemplateData["question"] + fmt.Sprintf("<tr><td>%c</td></tr>", char)
	}

	return questionTemplateData
}


func KanjiReading(p Part) map[string]string {

	//this will get filled out with the data for the question
	questionTemplateData := make(map[string]string) 


	for _, char := range p.Content {
		questionTemplateData["question"] = questionTemplateData["question"] + fmt.Sprintf("<tr><td><b>%c</b></td></tr>", char)
	}

	questionTemplateData["hints"] = "<tr><td><img src=\"images\\top_bracket.png\"/></td><tr>"
	for i := 0; i<p.Ans; i++ {
		questionTemplateData["hints"] = questionTemplateData["hints"] + "<tr><td> </td></tr>"
	}
	questionTemplateData["hints"] = questionTemplateData["hints"] + "<tr><td><img src=\"images\\btm_bracket.png\"/ with=\"50\"></td></tr>"

	return questionTemplateData
}

func KanjiWriting(p Part) map[string]string {
	//this will get filled out with the data for the question
	questionTemplateData := make(map[string]string) 

	//questionTemplateData["hints"] = "<tr><td>----</td><tr>"

	for _, char := range p.Content {
		questionTemplateData["hints"] = questionTemplateData["hints"] + fmt.Sprintf("<tr><td><b>%c</b></td></tr>", char)
	}

	//questionTemplateData["hints"] = questionTemplateData["hints"] + "<tr><td>----</td></tr>"

	return questionTemplateData
}

func CombinationWriting(p Part) map[string]string {
	//this will get filled out with the data for the question
	questionTemplateData := make(map[string]string) 

	//questionTemplateData["hints"] = "<tr><td>----</td><tr>"

	for i := 0; i<p.Ans; i++ {
		questionTemplateData["space"] = questionTemplateData["space"] + "<tr><td><img src='images\\empty.png' width='200'/></td></tr>"
	}


	for _, char := range p.Content {
		questionTemplateData["hints"] = questionTemplateData["hints"] + fmt.Sprintf("<tr><td><b>%c</b></td></tr>", char)
	}

	//questionTemplateData["hints"] = questionTemplateData["hints"] + "<tr><td>----</td></tr>"

	return questionTemplateData
}

Onwards and upwards ….