From 1036ea930f240a0f0ff14f78e7df329a32f90732 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 20 Jul 2023 13:18:03 -0700 Subject: [PATCH 1/3] Rename monorepo from shiritori-go to jichanorg --- cmd/shiritori/main.go | 4 ++-- go.mod | 2 +- shiritori/api/events.go | 4 ++-- shiritori/api/submit.go | 4 ++-- shiritori/clientset.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/shiritori/main.go b/cmd/shiritori/main.go index 7e71134..57f62ea 100644 --- a/cmd/shiritori/main.go +++ b/cmd/shiritori/main.go @@ -4,8 +4,8 @@ import ( "log" "net/http" - . "git.elnu.com/ElnuDev/shiritori-go/shiritori" - "git.elnu.com/ElnuDev/shiritori-go/shiritori/api" + . "git.elnu.com/ElnuDev/jichanorg/shiritori" + "git.elnu.com/ElnuDev/jichanorg/shiritori/api" ) func main() { diff --git a/go.mod b/go.mod index c3320da..4010d61 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module git.elnu.com/ElnuDev/shiritori-go +module git.elnu.com/ElnuDev/jichanorg go 1.20 \ No newline at end of file diff --git a/shiritori/api/events.go b/shiritori/api/events.go index 3cea08b..f896b89 100644 --- a/shiritori/api/events.go +++ b/shiritori/api/events.go @@ -4,8 +4,8 @@ import ( "fmt" "net/http" - "git.elnu.com/ElnuDev/shiritori-go/httputils" - . "git.elnu.com/ElnuDev/shiritori-go/shiritori" + "git.elnu.com/ElnuDev/jichanorg/httputils" + . "git.elnu.com/ElnuDev/jichanorg/shiritori" ) func GenerateApiEvents(clients *ClientSet) httputils.Handler { diff --git a/shiritori/api/submit.go b/shiritori/api/submit.go index 0d46aee..115b153 100644 --- a/shiritori/api/submit.go +++ b/shiritori/api/submit.go @@ -3,8 +3,8 @@ package api import ( "net/http" - "git.elnu.com/ElnuDev/shiritori-go/httputils" - . "git.elnu.com/ElnuDev/shiritori-go/shiritori" + "git.elnu.com/ElnuDev/jichanorg/httputils" + . "git.elnu.com/ElnuDev/jichanorg/shiritori" ) func GenerateApiSubmit(clients *ClientSet) httputils.Handler { diff --git a/shiritori/clientset.go b/shiritori/clientset.go index 27e7428..4cfd6b5 100644 --- a/shiritori/clientset.go +++ b/shiritori/clientset.go @@ -3,7 +3,7 @@ package shiritori import ( "fmt" - "git.elnu.com/ElnuDev/shiritori-go/httputils" + "git.elnu.com/ElnuDev/jichanorg/httputils" ) type Client = chan []httputils.SseEvent From f8ccf62570aca31a78bd49327c429f9b0d75d10f Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 20 Jul 2023 13:31:07 -0700 Subject: [PATCH 2/3] Proper go.work implementation --- go.work | 5 +++++ httputils/go.mod | 3 +++ shiritori/api/events.go | 2 +- shiritori/api/submit.go | 2 +- shiritori/{clientset.go => clients/clients.go} | 2 +- shiritori/go.mod | 3 +++ {cmd/shiritori => shiritori}/main.go | 2 +- {cmd/shiritori => shiritori}/static/index.html | 0 {cmd/shiritori => shiritori}/static/style.css | 0 9 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 go.work create mode 100644 httputils/go.mod rename shiritori/{clientset.go => clients/clients.go} (99%) create mode 100644 shiritori/go.mod rename {cmd/shiritori => shiritori}/main.go (87%) rename {cmd/shiritori => shiritori}/static/index.html (100%) rename {cmd/shiritori => shiritori}/static/style.css (100%) diff --git a/go.work b/go.work new file mode 100644 index 0000000..c8b5819 --- /dev/null +++ b/go.work @@ -0,0 +1,5 @@ +go 1.20 + +use ./httputils +use ./dict +use ./shiritori \ No newline at end of file diff --git a/httputils/go.mod b/httputils/go.mod new file mode 100644 index 0000000..294c2f4 --- /dev/null +++ b/httputils/go.mod @@ -0,0 +1,3 @@ +module git.elnu.com/ElnuDev/jichanorg/httputils + +go 1.20 \ No newline at end of file diff --git a/shiritori/api/events.go b/shiritori/api/events.go index f896b89..744c4cc 100644 --- a/shiritori/api/events.go +++ b/shiritori/api/events.go @@ -5,7 +5,7 @@ import ( "net/http" "git.elnu.com/ElnuDev/jichanorg/httputils" - . "git.elnu.com/ElnuDev/jichanorg/shiritori" + . "git.elnu.com/ElnuDev/jichanorg/shiritori/clients" ) func GenerateApiEvents(clients *ClientSet) httputils.Handler { diff --git a/shiritori/api/submit.go b/shiritori/api/submit.go index 115b153..782abc0 100644 --- a/shiritori/api/submit.go +++ b/shiritori/api/submit.go @@ -4,7 +4,7 @@ import ( "net/http" "git.elnu.com/ElnuDev/jichanorg/httputils" - . "git.elnu.com/ElnuDev/jichanorg/shiritori" + . "git.elnu.com/ElnuDev/jichanorg/shiritori/clients" ) func GenerateApiSubmit(clients *ClientSet) httputils.Handler { diff --git a/shiritori/clientset.go b/shiritori/clients/clients.go similarity index 99% rename from shiritori/clientset.go rename to shiritori/clients/clients.go index 4cfd6b5..8b7d1da 100644 --- a/shiritori/clientset.go +++ b/shiritori/clients/clients.go @@ -1,4 +1,4 @@ -package shiritori +package clients import ( "fmt" diff --git a/shiritori/go.mod b/shiritori/go.mod new file mode 100644 index 0000000..a7956a2 --- /dev/null +++ b/shiritori/go.mod @@ -0,0 +1,3 @@ +module git.elnu.com/ElnuDev/jichanorg/shiritori + +go 1.20 \ No newline at end of file diff --git a/cmd/shiritori/main.go b/shiritori/main.go similarity index 87% rename from cmd/shiritori/main.go rename to shiritori/main.go index 57f62ea..1a2266b 100644 --- a/cmd/shiritori/main.go +++ b/shiritori/main.go @@ -4,8 +4,8 @@ import ( "log" "net/http" - . "git.elnu.com/ElnuDev/jichanorg/shiritori" "git.elnu.com/ElnuDev/jichanorg/shiritori/api" + . "git.elnu.com/ElnuDev/jichanorg/shiritori/clients" ) func main() { diff --git a/cmd/shiritori/static/index.html b/shiritori/static/index.html similarity index 100% rename from cmd/shiritori/static/index.html rename to shiritori/static/index.html diff --git a/cmd/shiritori/static/style.css b/shiritori/static/style.css similarity index 100% rename from cmd/shiritori/static/style.css rename to shiritori/static/style.css From 01204ffc81072e51f608a6b540b9ed8a0df52d8d Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 20 Jul 2023 19:31:26 -0700 Subject: [PATCH 3/3] dict: MVP --- dict/.gitignore | 1 + dict/README.md | 14 +++++ dict/go.mod | 8 +++ dict/go.sum | 4 ++ dict/main.go | 112 +++++++++++++++++++++++++++++++++++++ dict/static/index.html | 25 +++++++++ dict/templates/search.html | 21 +++++++ httputils/handler.go | 7 ++- shiritori/api/submit.go | 3 +- 9 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 dict/.gitignore create mode 100644 dict/README.md create mode 100644 dict/go.mod create mode 100644 dict/go.sum create mode 100644 dict/main.go create mode 100644 dict/static/index.html create mode 100644 dict/templates/search.html diff --git a/dict/.gitignore b/dict/.gitignore new file mode 100644 index 0000000..30e4d8d --- /dev/null +++ b/dict/.gitignore @@ -0,0 +1 @@ +JMdict.xml \ No newline at end of file diff --git a/dict/README.md b/dict/README.md new file mode 100644 index 0000000..bb0c063 --- /dev/null +++ b/dict/README.md @@ -0,0 +1,14 @@ +# jichanorg/dict + +**jichanorg/dict** is a [hypermedia](https://hypermedia.systems/) and JSON API for parsing the JMDict Japanese dictionary project. + +Its primary goals are: + +- be a self-hostable dictionary solution to replace existing proprietary ([Jisho.org](https://jisho.org/)) and partially proprietary ([Jotoba](https://jotoba.de/)) dictionaries. +- Provide a hypermedia API for integration into other applications, such as [jichanorg/shiritori](../shiritori). + +### Configuration + +1. [Download the latest version of JMdict](https://www.edrdg.org/wiki/index.php/JMdict-EDICT_Dictionary_Project#CURRENT_VERSION_&_DOWNLOAD). For development, download the file with only English glosses, which will substantially decrease parsing time. +2. Extract the archive +3. Rename the file to JMDict.xml \ No newline at end of file diff --git a/dict/go.mod b/dict/go.mod new file mode 100644 index 0000000..c0b1214 --- /dev/null +++ b/dict/go.mod @@ -0,0 +1,8 @@ +module git.elnu.com/ElnuDev/jichanorg/dict + +go 1.20 + +require ( + foosoft.net/projects/jmdict v0.0.0-20220714211640-cc9bc30b68a3 // indirect + github.com/gorilla/mux v1.8.0 // indirect +) diff --git a/dict/go.sum b/dict/go.sum new file mode 100644 index 0000000..7ae614e --- /dev/null +++ b/dict/go.sum @@ -0,0 +1,4 @@ +foosoft.net/projects/jmdict v0.0.0-20220714211640-cc9bc30b68a3 h1:zjHGpgUR2WP3pf6NVZM38OKYNse0GjovCW2v23V72PQ= +foosoft.net/projects/jmdict v0.0.0-20220714211640-cc9bc30b68a3/go.mod h1:ZrjLCcE7ZrND28ZOSGYMd78tL+Dffiv2g+NjOMKgnew= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/dict/main.go b/dict/main.go new file mode 100644 index 0000000..77755a9 --- /dev/null +++ b/dict/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + + "foosoft.net/projects/jmdict" + "git.elnu.com/ElnuDev/jichanorg/httputils" + "github.com/gorilla/mux" +) + +var dict jmdict.Jmdict + +func LoadDict() error { + const jmdictFile = "JMdict.xml" + reader, err := os.Open(jmdictFile) + if err != nil { + return err + } + dict, _, err = jmdict.LoadJmdict(reader) + if err != nil { + return err + } + return nil +} + +type Entry struct { + Kanji string + Reading string + Definitions []string +} + +func ParseEntry(entry jmdict.JmdictEntry) Entry { + kanji := "" + if len(entry.Kanji) > 0 { + kanji = entry.Kanji[0].Expression + } + reading := "" + if len(entry.Readings) > 0 { + reading = entry.Readings[0].Reading + } + var definitions []string + if len(entry.Sense) > 0 && len(entry.Sense[0].Glossary) > 0 { + definitions = make([]string, len(entry.Sense[0].Glossary)) + for i, glossary := range entry.Sense[0].Glossary { + definitions[i] = glossary.Content + } + } + return Entry{ + Kanji: kanji, + Reading: reading, + Definitions: definitions, + } +} + +func Search(query string) []Entry { + entries := make([]Entry, 0) + for _, jmdictEntry := range dict.Entries { + for _, kanji := range jmdictEntry.Kanji { + if kanji.Expression == query { + goto match + } + } + for _, reading := range jmdictEntry.Readings { + if reading.Reading == query { + goto match + } + } + continue + match: + entry := ParseEntry(jmdictEntry) + entries = append(entries, entry) + } + return entries +} + +func main() { + err := LoadDict() + if err != nil { + fmt.Println(err) + return + } + fmt.Println("JMdict loaded!") + r := mux.NewRouter() + r.HandleFunc("/api/search", httputils.GenerateHandler( + "search.html", + func(w http.ResponseWriter, r *http.Request) bool { + if r.Header.Get("Accept") != "application/json" { + return true + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + r.ParseMultipartForm(0) + query := r.FormValue("q") + entries := Search(query) + jsonBytes, _ := json.Marshal(entries) + 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 + }, + []string{http.MethodGet, http.MethodPost}, + )) + r.Handle("/", http.FileServer(http.Dir("static"))) + log.Fatal(http.ListenAndServe(":3334", r)) +} diff --git a/dict/static/index.html b/dict/static/index.html new file mode 100644 index 0000000..cfebb64 --- /dev/null +++ b/dict/static/index.html @@ -0,0 +1,25 @@ + + + + + + jidict + + + + +
+ + + +
+ +
+
+
+
+ + \ No newline at end of file diff --git a/dict/templates/search.html b/dict/templates/search.html new file mode 100644 index 0000000..b87fb1c --- /dev/null +++ b/dict/templates/search.html @@ -0,0 +1,21 @@ +

{{ $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 -}} +

{{- index .Definitions 0 -}}

+ {{- else -}} +
    + {{- range .Definitions }} +
  1. {{- . -}}
  2. + {{- end }} +
+ {{- end }} +
+{{ end -}} \ No newline at end of file diff --git a/httputils/handler.go b/httputils/handler.go index b9a2907..31bf89b 100644 --- a/httputils/handler.go +++ b/httputils/handler.go @@ -10,7 +10,7 @@ type Handler = func(http.ResponseWriter, *http.Request) func GenerateHandler( file string, - handler func(http.ResponseWriter, *http.Request), + handler func(http.ResponseWriter, *http.Request) bool, data func(http.ResponseWriter, *http.Request) any, methods []string, ) Handler { @@ -19,6 +19,7 @@ func GenerateHandler( 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))) for _, method := range methods { if method == r.Method { goto ok @@ -27,8 +28,8 @@ func GenerateHandler( w.WriteHeader(http.StatusMethodNotAllowed) return ok: - handler(w, r) - if tmpl != nil { + renderTemplate := handler(w, r) + if renderTemplate && tmpl != nil { w.Header().Set("Content-Type", "text/html; charset=utf-8") tmpl.Execute(w, data(w, r)) } diff --git a/shiritori/api/submit.go b/shiritori/api/submit.go index 782abc0..e1c9c5f 100644 --- a/shiritori/api/submit.go +++ b/shiritori/api/submit.go @@ -10,9 +10,10 @@ import ( func GenerateApiSubmit(clients *ClientSet) httputils.Handler { return httputils.GenerateHandler( "", - func(w http.ResponseWriter, r *http.Request) { + func(w http.ResponseWriter, r *http.Request) bool { r.ParseMultipartForm(0) clients.BroadcastWord(r.FormValue("word")) + return true }, nil, []string{http.MethodPost},