From e00df6aac60c44d847af0086d9e5aa4b054dc0bc Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 09:34:43 -0700 Subject: [PATCH 1/9] Add clarification for debug template reloading --- httputils/handler.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/httputils/handler.go b/httputils/handler.go index 31bf89b..de941ce 100644 --- a/httputils/handler.go +++ b/httputils/handler.go @@ -15,11 +15,14 @@ func GenerateHandler( methods []string, ) Handler { var tmpl *template.Template - if file != "" { - tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file))) - } + //if file != "" { + // tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file))) + //} return func(w http.ResponseWriter, r *http.Request) { - tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file))) + // Remove in production, enables live template reloading + if file != "" { + tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file))) + } for _, method := range methods { if method == r.Method { goto ok From 442f727d8a2ee3dd5a64f4cdf0cd1c35104dd909 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 10:34:56 -0700 Subject: [PATCH 2/9] Add hot tempalte reloading, WIP pos --- dict/main.go | 20 ++++++++--- dict/templates/definition.html | 3 ++ dict/templates/search.html | 4 +-- httputils/handler.go | 61 +++++++++++++++++++++++++++++----- 4 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 dict/templates/definition.html diff --git a/dict/main.go b/dict/main.go index 77755a9..6c7a0d0 100644 --- a/dict/main.go +++ b/dict/main.go @@ -30,7 +30,12 @@ func LoadDict() error { type Entry struct { Kanji string Reading string - Definitions []string + Definitions []Definition +} + +type Definition struct { + Definition string + PartOfSpeech string } func ParseEntry(entry jmdict.JmdictEntry) Entry { @@ -42,11 +47,18 @@ func ParseEntry(entry jmdict.JmdictEntry) Entry { if len(entry.Readings) > 0 { reading = entry.Readings[0].Reading } - var definitions []string + var definitions []Definition if len(entry.Sense) > 0 && len(entry.Sense[0].Glossary) > 0 { - definitions = make([]string, len(entry.Sense[0].Glossary)) + definitions = make([]Definition, len(entry.Sense[0].Glossary)) for i, glossary := range entry.Sense[0].Glossary { - definitions[i] = glossary.Content + var pos string + if len(entry.Sense[0].PartsOfSpeech) > i+1 { + pos = entry.Sense[0].PartsOfSpeech[i] + } + definitions[i] = Definition{ + Definition: glossary.Content, + PartOfSpeech: pos, + } } } return Entry{ diff --git a/dict/templates/definition.html b/dict/templates/definition.html new file mode 100644 index 0000000..b85c3ed --- /dev/null +++ b/dict/templates/definition.html @@ -0,0 +1,3 @@ +{{ define "definition" }} +{{ if .PartOfSpeech }}{{ .PartOfSpeech }} {{ end }}{{ .Definition -}} +{{ end }} \ No newline at end of file diff --git a/dict/templates/search.html b/dict/templates/search.html index b87fb1c..3ee8e32 100644 --- a/dict/templates/search.html +++ b/dict/templates/search.html @@ -9,11 +9,11 @@ {{- end -}} {{ if le (len .Definitions) 2 -}} -

{{- index .Definitions 0 -}}

+

{{- template "definition" (index .Definitions 0) -}}

{{- else -}}
    {{- range .Definitions }} -
  1. {{- . -}}
  2. +
  3. {{- template "definition" . -}}
  4. {{- end }}
{{- end }} diff --git a/httputils/handler.go b/httputils/handler.go index de941ce..a98c763 100644 --- a/httputils/handler.go +++ b/httputils/handler.go @@ -3,25 +3,68 @@ package httputils import ( "fmt" "net/http" + "os" + "path/filepath" + "strings" "text/template" + "time" ) type Handler = func(http.ResponseWriter, *http.Request) +const templateFolder = "templates" + +var templatePaths, templateModTimes, _ = getTemplates() +var templates *template.Template = template.Must(template.ParseFiles(templatePaths...)) + +func getTemplates() ([]string, map[string]time.Time, error) { + var modTimes map[string]time.Time = make(map[string]time.Time) + err := filepath.Walk(templateFolder, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(path, ".html") { + modTimes[path] = info.ModTime() + } + return nil + }) + paths := make([]string, len(modTimes)) + i := 0 + for path := range modTimes { + paths[i] = path + i++ + } + return paths, modTimes, err +} + +func reloadTemplateIfModified(path string) { + fileInfo, _ := os.Stat(path) + modTime := fileInfo.ModTime() + if modTime.After(templateModTimes[path]) { + fmt.Printf("Reloading template %s...\n", path) + templates.ParseFiles(path) + templateModTimes[path] = modTime + } +} + +func reloadTemplatesIfModified() { + for _, path := range templatePaths { + reloadTemplateIfModified(path) + } +} + +const reloadTemplates = true + func GenerateHandler( file string, handler func(http.ResponseWriter, *http.Request) bool, data func(http.ResponseWriter, *http.Request) any, methods []string, ) Handler { - var tmpl *template.Template - //if file != "" { - // tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file))) - //} return func(w http.ResponseWriter, r *http.Request) { - // Remove in production, enables live template reloading - if file != "" { - tmpl = template.Must(template.ParseFiles(fmt.Sprintf("templates/%s", file))) + // All templates must be reloaded in case of dependencies + if reloadTemplates { + reloadTemplatesIfModified() } for _, method := range methods { if method == r.Method { @@ -32,9 +75,9 @@ func GenerateHandler( return ok: renderTemplate := handler(w, r) - if renderTemplate && tmpl != nil { + if renderTemplate && file != "" { w.Header().Set("Content-Type", "text/html; charset=utf-8") - tmpl.Execute(w, data(w, r)) + templates.ExecuteTemplate(w, file, data(w, r)) } } } From b5b45cc4affea141e3d7ceb3dc16544dfc2fcc41 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 10:37:55 -0700 Subject: [PATCH 3/9] Trim query whitespace --- dict/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dict/main.go b/dict/main.go index 6c7a0d0..6ba40da 100644 --- a/dict/main.go +++ b/dict/main.go @@ -6,6 +6,7 @@ import ( "log" "net/http" "os" + "strings" "foosoft.net/projects/jmdict" "git.elnu.com/ElnuDev/jichanorg/httputils" @@ -69,6 +70,7 @@ func ParseEntry(entry jmdict.JmdictEntry) Entry { } func Search(query string) []Entry { + query = strings.TrimSpace(query) entries := make([]Entry, 0) for _, jmdictEntry := range dict.Entries { for _, kanji := range jmdictEntry.Kanji { From b99ebc787d5a734f889a7f67c8a4904c28f84774 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 10:57:54 -0700 Subject: [PATCH 4/9] Improve definition readouts --- dict/main.go | 22 +++++++++++----------- dict/static/index.html | 5 +++++ dict/templates/definition.html | 4 ++-- dict/templates/search.html | 6 ++++-- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/dict/main.go b/dict/main.go index 6ba40da..422f92e 100644 --- a/dict/main.go +++ b/dict/main.go @@ -36,7 +36,7 @@ type Entry struct { type Definition struct { Definition string - PartOfSpeech string + PartOfSpeech []string } func ParseEntry(entry jmdict.JmdictEntry) Entry { @@ -49,18 +49,18 @@ func ParseEntry(entry jmdict.JmdictEntry) Entry { reading = entry.Readings[0].Reading } var definitions []Definition - if len(entry.Sense) > 0 && len(entry.Sense[0].Glossary) > 0 { - definitions = make([]Definition, len(entry.Sense[0].Glossary)) - for i, glossary := range entry.Sense[0].Glossary { - var pos string - if len(entry.Sense[0].PartsOfSpeech) > i+1 { - pos = entry.Sense[0].PartsOfSpeech[i] - } - definitions[i] = Definition{ - Definition: glossary.Content, - PartOfSpeech: pos, + definitions = make([]Definition, len(entry.Sense)) + for i, sense := range entry.Sense { + definition := sense.Glossary[0].Content + if len(sense.Glossary) > 1 { + for _, glossary := range sense.Glossary[1:] { + definition += "; " + glossary.Content } } + definitions[i] = Definition{ + Definition: definition, + PartOfSpeech: sense.PartsOfSpeech, + } } return Entry{ Kanji: kanji, diff --git a/dict/static/index.html b/dict/static/index.html index cfebb64..3ebf623 100644 --- a/dict/static/index.html +++ b/dict/static/index.html @@ -5,6 +5,11 @@ jidict + diff --git a/dict/templates/definition.html b/dict/templates/definition.html index b85c3ed..86595c7 100644 --- a/dict/templates/definition.html +++ b/dict/templates/definition.html @@ -1,3 +1,3 @@ -{{ define "definition" }} -{{ if .PartOfSpeech }}{{ .PartOfSpeech }} {{ end }}{{ .Definition -}} +{{- define "definition" -}} +{{ if .PartOfSpeech }}{{ .PartOfSpeech }}
{{ end }}{{ .Definition -}} {{ end }} \ No newline at end of file diff --git a/dict/templates/search.html b/dict/templates/search.html index 3ee8e32..7679286 100644 --- a/dict/templates/search.html +++ b/dict/templates/search.html @@ -1,5 +1,5 @@

{{ $count := (len .) }}{{ if eq $count 0 }}No results{{ else }}{{ $count }} result{{ if ne $count 1}}s{{ end }}{{ end }}.

-{{- range . -}} +{{ range . -}}

{{- if .Kanji -}} @@ -13,7 +13,9 @@ {{- else -}}
    {{- range .Definitions }} -
  1. {{- template "definition" . -}}
  2. +
  3. + {{ template "definition" . }} +
  4. {{- end }}
{{- end }} From fb045b9276153a7f016371265679af8b51badc10 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 11:28:33 -0700 Subject: [PATCH 5/9] Implement exact results --- dict/main.go | 37 ++++++++++++++++++++++++++++--------- dict/templates/search.html | 31 ++++++++----------------------- dict/templates/word.html | 22 ++++++++++++++++++++++ 3 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 dict/templates/word.html diff --git a/dict/main.go b/dict/main.go index 422f92e..a82b718 100644 --- a/dict/main.go +++ b/dict/main.go @@ -69,26 +69,37 @@ func ParseEntry(entry jmdict.JmdictEntry) Entry { } } -func Search(query string) []Entry { +func Search(query string) (exactResults []Entry, otherResults []Entry) { query = strings.TrimSpace(query) - entries := make([]Entry, 0) + exactResults = make([]Entry, 0) + otherResults = make([]Entry, 0) for _, jmdictEntry := range dict.Entries { + exactMatch := false for _, kanji := range jmdictEntry.Kanji { if kanji.Expression == query { + exactMatch = true + goto match + } + if strings.Contains(kanji.Expression, query) { goto match } } + // TODO: Skip if query contains kanji for _, reading := range jmdictEntry.Readings { - if reading.Reading == query { + if strings.Contains(reading.Reading, query) { goto match } } continue match: entry := ParseEntry(jmdictEntry) - entries = append(entries, entry) + if exactMatch { + exactResults = append(exactResults, entry) + } else { + otherResults = append(otherResults, entry) + } } - return entries + return } func main() { @@ -108,16 +119,24 @@ func main() { w.Header().Set("Content-Type", "application/json; charset=utf-8") r.ParseMultipartForm(0) query := r.FormValue("q") - entries := Search(query) - jsonBytes, _ := json.Marshal(entries) + exactResults, otherResults := Search(query) + jsonBytes, _ := json.Marshal(append(exactResults, otherResults...)) fmt.Fprint(w, string(jsonBytes)) return false }, func(w http.ResponseWriter, r *http.Request) any { r.ParseMultipartForm(0) query := r.FormValue("q") - entry := Search(query) - return entry + exactResults, otherResults := Search(query) + return struct { + ExactResults []Entry + OtherResults []Entry + Count int + }{ + ExactResults: exactResults, + OtherResults: otherResults, + Count: len(exactResults) + len(otherResults), + } }, []string{http.MethodGet, http.MethodPost}, )) diff --git a/dict/templates/search.html b/dict/templates/search.html index 7679286..a345fe2 100644 --- a/dict/templates/search.html +++ b/dict/templates/search.html @@ -1,23 +1,8 @@ -

{{ $count := (len .) }}{{ if eq $count 0 }}No results{{ else }}{{ $count }} result{{ if ne $count 1}}s{{ end }}{{ end }}.

-{{ range . -}} -
-

- {{- if .Kanji -}} - {{- .Kanji -}}({{- .Reading -}}) - {{- else -}} - {{- .Reading -}} - {{- end -}} -

- {{ if le (len .Definitions) 2 -}} -

{{- template "definition" (index .Definitions 0) -}}

- {{- else -}} -
    - {{- range .Definitions }} -
  1. - {{ template "definition" . }} -
  2. - {{- end }} -
- {{- end }} -
-{{ end -}} \ No newline at end of file +

{{ if eq .Count 0 }}No results{{ else }}{{ .Count }} result{{ if ne .Count 1}}s{{ end }}{{ end }}.

+{{- range .ExactResults -}} +{{- template "word" . -}} +{{- end }} +
+{{ range .OtherResults -}} +{{ template "word" . }} +{{- end -}} \ No newline at end of file diff --git a/dict/templates/word.html b/dict/templates/word.html new file mode 100644 index 0000000..40d56f8 --- /dev/null +++ b/dict/templates/word.html @@ -0,0 +1,22 @@ +{{ define "word" }} +
+

+ {{- if .Kanji -}} + {{- .Kanji -}}({{- .Reading -}}) + {{- else -}} + {{- .Reading -}} + {{- end -}} +

+ {{ if le (len .Definitions) 2 -}} +

{{- template "definition" (index .Definitions 0) -}}

+ {{- else -}} +
    + {{- range .Definitions }} +
  1. + {{ template "definition" . }} +
  2. + {{- end }} +
+ {{- end }} +
+{{ end }} \ No newline at end of file From 36710478f2787aa2a4c637142791aad78ab82c91 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 12:17:11 -0700 Subject: [PATCH 6/9] Implement route handling --- dict/main.go | 48 +++++++++++++++++++++------ dict/{static => templates}/index.html | 7 ++-- dict/templates/search.html | 5 ++- 3 files changed, 47 insertions(+), 13 deletions(-) rename dict/{static => templates}/index.html (65%) diff --git a/dict/main.go b/dict/main.go index a82b718..c2e5dde 100644 --- a/dict/main.go +++ b/dict/main.go @@ -102,6 +102,20 @@ func Search(query string) (exactResults []Entry, otherResults []Entry) { return } +type searchTemplateData struct { + ExactResults []Entry + OtherResults []Entry + Count int +} + +func initSearchTemplateData(exactResults []Entry, otherResults []Entry) searchTemplateData { + return searchTemplateData{ + ExactResults: exactResults, + OtherResults: otherResults, + Count: len(exactResults) + len(otherResults), + } +} + func main() { err := LoadDict() if err != nil { @@ -110,6 +124,29 @@ func main() { } fmt.Println("JMdict loaded!") r := mux.NewRouter() + r.HandleFunc("/", httputils.GenerateHandler( + "index.html", + func(w http.ResponseWriter, r *http.Request) bool { return true }, + func(w http.ResponseWriter, r *http.Request) any { return nil }, + []string{http.MethodGet}, + )) + r.HandleFunc("/search/{query}", httputils.GenerateHandler( + "index.html", + func(w http.ResponseWriter, r *http.Request) bool { + return true + }, + func(w http.ResponseWriter, r *http.Request) any { + query := mux.Vars(r)["query"] + return struct { + Query string + Results searchTemplateData + }{ + Query: query, + Results: initSearchTemplateData(Search(query)), + } + }, + []string{http.MethodGet}, + )) r.HandleFunc("/api/search", httputils.GenerateHandler( "search.html", func(w http.ResponseWriter, r *http.Request) bool { @@ -127,16 +164,7 @@ func main() { func(w http.ResponseWriter, r *http.Request) any { r.ParseMultipartForm(0) query := r.FormValue("q") - exactResults, otherResults := Search(query) - return struct { - ExactResults []Entry - OtherResults []Entry - Count int - }{ - ExactResults: exactResults, - OtherResults: otherResults, - Count: len(exactResults) + len(otherResults), - } + return initSearchTemplateData(Search(query)) }, []string{http.MethodGet, http.MethodPost}, )) diff --git a/dict/static/index.html b/dict/templates/index.html similarity index 65% rename from dict/static/index.html rename to dict/templates/index.html index 3ebf623..a9232e8 100644 --- a/dict/static/index.html +++ b/dict/templates/index.html @@ -19,11 +19,14 @@
- +
-
+
+ {{ with .Results }}{{ template "search" . }}{{ end }} +

diff --git a/dict/templates/search.html b/dict/templates/search.html index a345fe2..0e077d4 100644 --- a/dict/templates/search.html +++ b/dict/templates/search.html @@ -1,3 +1,4 @@ +{{- define "search" -}}

{{ if eq .Count 0 }}No results{{ else }}{{ .Count }} result{{ if ne .Count 1}}s{{ end }}{{ end }}.

{{- range .ExactResults -}} {{- template "word" . -}} @@ -5,4 +6,6 @@
{{ range .OtherResults -}} {{ template "word" . }} -{{- end -}} \ No newline at end of file +{{- end -}} +{{- end -}} +{{- template "search" . -}} \ No newline at end of file From b6d3b703c9c05e30ecf61c37c920951174677971 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 12:17:54 -0700 Subject: [PATCH 7/9] Add result truncation --- dict/main.go | 14 +++++++++++--- dict/templates/search.html | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dict/main.go b/dict/main.go index c2e5dde..c240895 100644 --- a/dict/main.go +++ b/dict/main.go @@ -69,10 +69,11 @@ func ParseEntry(entry jmdict.JmdictEntry) Entry { } } -func Search(query string) (exactResults []Entry, otherResults []Entry) { +func Search(query string) (exactResults []Entry, otherResults []Entry, truncated bool) { query = strings.TrimSpace(query) exactResults = make([]Entry, 0) otherResults = make([]Entry, 0) + count := 0 for _, jmdictEntry := range dict.Entries { exactMatch := false for _, kanji := range jmdictEntry.Kanji { @@ -98,6 +99,11 @@ func Search(query string) (exactResults []Entry, otherResults []Entry) { } else { otherResults = append(otherResults, entry) } + count++ + if count >= 500 { + truncated = true + break + } } return } @@ -105,13 +111,15 @@ func Search(query string) (exactResults []Entry, otherResults []Entry) { type searchTemplateData struct { ExactResults []Entry OtherResults []Entry + Truncated bool Count int } -func initSearchTemplateData(exactResults []Entry, otherResults []Entry) searchTemplateData { +func initSearchTemplateData(exactResults []Entry, otherResults []Entry, truncated bool) searchTemplateData { return searchTemplateData{ ExactResults: exactResults, OtherResults: otherResults, + Truncated: truncated, Count: len(exactResults) + len(otherResults), } } @@ -156,7 +164,7 @@ func main() { w.Header().Set("Content-Type", "application/json; charset=utf-8") r.ParseMultipartForm(0) query := r.FormValue("q") - exactResults, otherResults := Search(query) + exactResults, otherResults, _ := Search(query) jsonBytes, _ := json.Marshal(append(exactResults, otherResults...)) fmt.Fprint(w, string(jsonBytes)) return false diff --git a/dict/templates/search.html b/dict/templates/search.html index 0e077d4..90053b1 100644 --- a/dict/templates/search.html +++ b/dict/templates/search.html @@ -1,5 +1,5 @@ {{- define "search" -}} -

{{ if eq .Count 0 }}No results{{ else }}{{ .Count }} result{{ if ne .Count 1}}s{{ end }}{{ end }}.

+

{{ if .Truncated }}Truncated results, showing first {{ .Count }}{{ else }}{{ if eq .Count 0 }}No results{{ else }}{{ .Count }} result{{ if ne .Count 1}}s{{ end }}{{ end }}{{ end }}.

{{- range .ExactResults -}} {{- template "word" . -}} {{- end }} From d18c9303c6c8e3556ff512752c7343dc51ed7810 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 12:25:13 -0700 Subject: [PATCH 8/9] Redirect /search and /search to / --- dict/main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dict/main.go b/dict/main.go index c240895..08a754b 100644 --- a/dict/main.go +++ b/dict/main.go @@ -138,6 +138,11 @@ func main() { func(w http.ResponseWriter, r *http.Request) any { return nil }, []string{http.MethodGet}, )) + redirectToHome := func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "/", http.StatusPermanentRedirect) + } + r.HandleFunc("/search", redirectToHome) + r.HandleFunc("/search/", redirectToHome) r.HandleFunc("/search/{query}", httputils.GenerateHandler( "index.html", func(w http.ResponseWriter, r *http.Request) bool { From 61c1ce55028d238bba4a91cb9b454218ea73ca62 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Fri, 21 Jul 2023 12:28:01 -0700 Subject: [PATCH 9/9] Use GET rather than POST for search --- dict/main.go | 2 +- dict/templates/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dict/main.go b/dict/main.go index 08a754b..8b4328b 100644 --- a/dict/main.go +++ b/dict/main.go @@ -179,7 +179,7 @@ func main() { query := r.FormValue("q") return initSearchTemplateData(Search(query)) }, - []string{http.MethodGet, http.MethodPost}, + []string{http.MethodGet}, )) r.Handle("/", http.FileServer(http.Dir("static"))) log.Fatal(http.ListenAndServe(":3334", r)) diff --git a/dict/templates/index.html b/dict/templates/index.html index a9232e8..ca01362 100644 --- a/dict/templates/index.html +++ b/dict/templates/index.html @@ -18,7 +18,7 @@