Basic search and display working

This commit is contained in:
Las Zenow 2012-07-30 23:23:38 +02:00
parent 17cb7765c1
commit c64226ffb9
12 changed files with 325 additions and 65 deletions

4
.gitignore vendored
View file

@ -1,4 +1,8 @@
books/ books/
cover/
img/ img/
trantor trantor
.*.swp .*.swp
upload/upload
upload/books
upload/cover

10
book.html Normal file
View file

@ -0,0 +1,10 @@
<h1>{{.Title}}</h1>
<p>{{.Description}}</p>
<img src="{{.Cover}}" alt="{{.Title}}" />
<ul>
<li><strong>Author:</strong> {{range .Author}}{{.}}, {{end}}</li>
<li><strong>Publisher:</strong> {{.Publisher}}</li>
<li><strong>Tags:</strong> {{range .Subject}}{{.}}, {{end}}</li>
<li><strong>Date:</strong> {{.Date}}</li>
<li><strong>Lang:</strong> {{range .Lang}}{{.}} {{end}}</li>
</ul>

View file

@ -1,9 +1,23 @@
package main package main
type Book struct { type Book struct {
Title []string Title string
Creator []string Author []string
Subject []string Contributor string
Lang []string Publisher string
Path string Description string
Subject []string
Date string
Lang []string
Type string
Format string
Source string
Relation string
Coverage string
Rights string
Meta string
Path string
Cover string
CoverSmall string
Keywords []string
} }

5
foot.html Normal file
View file

@ -0,0 +1,5 @@
<footer>
<p><small>Chief Librarian: Las Zenow (zenow@tormail.org)</small></p>
</footer>
</body>
</html>

6
front.html Normal file
View file

@ -0,0 +1,6 @@
<h1>Imperial Library of Trantor</h1>
<form action="/search/">
<input type="search" name="q" />
<input type="submit" value="Search">
</form>
<p><img src="/img/library.jpg" alt="library" /></p>

5
head.html Normal file
View file

@ -0,0 +1,5 @@
<html>
<head>
<title>Imperial Library of Trantor</title>
</head>
<body>

35
search.go Normal file
View file

@ -0,0 +1,35 @@
package main
import (
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"net/http"
"strings"
)
func buildQuery(q string) bson.M {
words := strings.Split(q, " ")
reg := make([]bson.RegEx, len(words))
for i, w := range words {
reg[i].Pattern = w
reg[i].Options = "i"
}
return bson.M{"keywords": bson.M{"$all": reg}}
}
type searchData struct {
Search string
Books []Book
}
func searchHandler(coll *mgo.Collection, w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
req := strings.Join(r.Form["q"], " ")
var res []Book
coll.Find(buildQuery(req)).All(&res)
loadTemplate(w, "search", searchData{req, res})
}

13
search.html Normal file
View file

@ -0,0 +1,13 @@
<h1>Search</h1>
<form action="/search/">
<input type="search" name="q" value="{{.Search}}"/>
<input type="submit" value="Search">
</form>
<ul>
{{with .Books}}
{{range .}}
<li><img src="{{.CoverSmall}}" alt="{{.Title}}" /><a href="/book/{{.Title}}">{{.Title}}</a> - {{range .Author}}{{.}}, {{end}}</li>
{{end}}
{{end}}
</ul>

63
trantor.go Normal file
View file

@ -0,0 +1,63 @@
package main
import (
"html/template"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"net/http"
)
const (
IP = "127.0.0.1"
DB_NAME = "trantor"
BOOKS_COLL = "books"
)
func loadTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
// TODO: when finish devel conver to global:
var templates = template.Must(template.ParseFiles("head.html", "foot.html", "front.html", "book.html", "search.html"))
err := templates.ExecuteTemplate(w, "head.html", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = templates.ExecuteTemplate(w, tmpl+".html", data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = templates.ExecuteTemplate(w, "foot.html", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func bookHandler(coll *mgo.Collection, w http.ResponseWriter, r *http.Request) {
var book Book
coll.Find(bson.M{"title": r.URL.Path[len("/book/"):]}).One(&book)
loadTemplate(w, "book", book)
}
func sendFile(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path[1:]
http.ServeFile(w, r, path)
}
func main() {
session, err := mgo.Dial(IP)
if err != nil {
panic(err)
}
defer session.Close()
coll := session.DB(DB_NAME).C(BOOKS_COLL)
http.HandleFunc("/book/", func(w http.ResponseWriter, r *http.Request) { bookHandler(coll, w, r) })
http.HandleFunc("/search/", func(w http.ResponseWriter, r *http.Request) { searchHandler(coll, w, r) })
http.HandleFunc("/img/", sendFile)
http.HandleFunc("/cover/", sendFile)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { loadTemplate(w, "front", nil) })
http.ListenAndServe(":8080", nil)
}

View file

@ -1,60 +0,0 @@
package main
import (
"fmt"
"os"
"git.gitorious.org/go-pkg/epub.git"
"labix.org/v2/mgo"
)
const (
IP = "127.0.0.1"
DB_NAME = "trantor"
BOOKS_COLL = "books"
PATH = "./books/"
)
func store(coll *mgo.Collection, path string) {
var book Book
e, err := epub.Open(path, 0)
if err != nil {
fmt.Println(path)
panic(err) // TODO: do something
}
defer e.Close()
// TODO: do it for all metadata
book.Title = e.Metadata(epub.EPUB_TITLE)
book.Creator = e.Metadata(epub.EPUB_CREATOR)
book.Subject = e.Metadata(epub.EPUB_SUBJECT)
book.Lang = e.Metadata(epub.EPUB_LANG)
book.Path = path
coll.Insert(book)
}
func main() {
session, err := mgo.Dial(IP)
if err != nil {
panic(err) // TODO: do something
}
defer session.Close()
coll := session.DB(DB_NAME).C(BOOKS_COLL)
f, err := os.Open(PATH)
if err != nil {
fmt.Println(PATH)
panic(err) // TODO: do something
}
names, err := f.Readdirnames(0)
if err != nil {
panic(err) // TODO: do something
}
for _, name := range names {
store(coll, PATH + name)
}
}

23
upload/database.go Normal file
View file

@ -0,0 +1,23 @@
package main
type Book struct {
Title string
Author []string
Contributor string
Publisher string
Description string
Subject []string
Date string
Lang []string
Type string
Format string
Source string
Relation string
Coverage string
Rights string
Meta string
Path string
Cover string
CoverSmall string
Keywords []string
}

142
upload/upload.go Normal file
View file

@ -0,0 +1,142 @@
package main
import (
"fmt"
"git.gitorious.org/go-pkg/epub.git"
"labix.org/v2/mgo"
"os"
"os/exec"
"regexp"
"strings"
)
const (
IP = "127.0.0.1"
DB_NAME = "trantor"
BOOKS_COLL = "books"
PATH = "books/"
COVER_PATH = "cover/"
RESIZE = "/usr/bin/convert -resize 300 -quality 60 "
RESIZE_THUMB = "/usr/bin/convert -resize 60 -quality 60 "
)
func getCover(e *epub.Epub, path string) (string, string) {
exp, _ := regexp.Compile("<img.*src=[\"']([^\"']*(\\.[^\\.]*))[\"']")
it := e.Iterator(epub.EITERATOR_SPINE)
defer it.Close()
var err error = nil
txt := it.Curr()
for err == nil {
res := exp.FindStringSubmatch(txt)
if res != nil {
imgPath := COVER_PATH + path + res[2]
f, _ := os.Create(imgPath)
f.Write(e.Data(res[1]))
resize := append(strings.Split(RESIZE, " "), imgPath, imgPath)
cmd := exec.Command(resize[0], resize[1:]...)
cmd.Run()
imgPathSmall := COVER_PATH + path + "_small" + res[2]
resize = append(strings.Split(RESIZE_THUMB, " "), imgPath, imgPathSmall)
cmd = exec.Command(resize[0], resize[1:]...)
cmd.Run()
return "/" + imgPath, "/" + imgPathSmall
}
txt, err = it.Next()
}
return "", ""
}
func parseAuthr(creator []string) []string {
exp1, _ := regexp.Compile("^(.*\\( *([^\\)]*) *\\))*$")
exp2, _ := regexp.Compile("^[^:]*: *(.*)$")
var res []string //TODO: can be predicted the lenght
for _, s := range creator {
auth := exp1.FindStringSubmatch(s)
if auth != nil {
res = append(res, strings.Join(auth[2:], ", "))
} else {
auth := exp2.FindStringSubmatch(s)
if auth != nil {
res = append(res, auth[1])
} else {
res = append(res, s)
}
}
}
return res
}
func parseSubject(subject []string) []string {
var res []string
for _, s := range subject {
res = append(res, strings.Split(s, " / ")...) // FIXME
}
return res
}
func keywords(b Book) (k []string) {
k = strings.Split(b.Title, " ")
for _, a := range b.Author {
k = append(k, strings.Split(a, " ")...)
}
k = append(k, strings.Split(b.Publisher, " ")...)
k = append(k, b.Subject...)
return
}
func store(coll *mgo.Collection, path string) {
var book Book
e, err := epub.Open(PATH+path, 0)
if err != nil {
fmt.Println(path)
panic(err) // TODO: do something
}
defer e.Close()
// TODO: do it for all metadata
book.Title = strings.Join(e.Metadata(epub.EPUB_TITLE), ", ")
book.Author = parseAuthr(e.Metadata(epub.EPUB_CREATOR))
book.Contributor = strings.Join(e.Metadata(epub.EPUB_CONTRIB), ", ")
book.Publisher = strings.Join(e.Metadata(epub.EPUB_PUBLISHER), ", ")
book.Description = strings.Join(e.Metadata(epub.EPUB_DESCRIPTION), ", ")
book.Subject = parseSubject(e.Metadata(epub.EPUB_SUBJECT))
book.Date = strings.Join(e.Metadata(epub.EPUB_DATE), ", ")
book.Lang = e.Metadata(epub.EPUB_LANG)
book.Type = strings.Join(e.Metadata(epub.EPUB_TYPE), ", ")
book.Format = strings.Join(e.Metadata(epub.EPUB_FORMAT), ", ")
book.Source = strings.Join(e.Metadata(epub.EPUB_SOURCE), ", ")
book.Relation = strings.Join(e.Metadata(epub.EPUB_RELATION), ", ")
book.Coverage = strings.Join(e.Metadata(epub.EPUB_COVERAGE), ", ")
book.Rights = strings.Join(e.Metadata(epub.EPUB_RIGHTS), ", ")
book.Meta = strings.Join(e.Metadata(epub.EPUB_META), ", ")
book.Path = PATH + path
book.Cover, book.CoverSmall = getCover(e, path)
book.Keywords = keywords(book)
coll.Insert(book)
}
func main() {
session, err := mgo.Dial(IP)
if err != nil {
panic(err) // TODO: do something
}
defer session.Close()
coll := session.DB(DB_NAME).C(BOOKS_COLL)
f, err := os.Open(PATH)
if err != nil {
fmt.Println(PATH)
panic(err) // TODO: do something
}
names, err := f.Readdirnames(0)
if err != nil {
panic(err) // TODO: do something
}
for _, name := range names {
store(coll, name)
}
}