Factorize go

This commit is contained in:
Meutel 2017-08-06 18:49:40 +02:00
parent 606afb279d
commit e9257fe6a7
6 changed files with 137 additions and 193 deletions

View File

@ -219,12 +219,9 @@ type ReqParams struct {
AllWords bool AllWords bool
} }
func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) error { // add functions to templates
return app.Template.ExecuteTemplate(res, tpl, model)
}
func TemplatesFunc() *template.Template { func TemplatesFunc() *template.Template {
tpl := template.New("") return template.New("").Funcs(template.FuncMap{
tpl.Funcs(template.FuncMap{
"humanSize": func(sz int64) string { "humanSize": func(sz int64) string {
return datasize.ByteSize(sz).HumanReadable() return datasize.ByteSize(sz).HumanReadable()
}, },
@ -236,17 +233,26 @@ func TemplatesFunc() *template.Template {
return "/calibre/" + url.PathEscape(book.Path) + "/" + url.PathEscape(data.Name) + "." + strings.ToLower(data.Format) return "/calibre/" + url.PathEscape(book.Path) + "/" + url.PathEscape(data.Name) + "." + strings.ToLower(data.Format)
}, },
}) })
return tpl
} }
// output page with template
func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) error {
return app.Template.ExecuteTemplate(res, tpl, model)
}
// output as JSON
func writeJson(res http.ResponseWriter, model interface{}) error { func writeJson(res http.ResponseWriter, model interface{}) error {
res.Header().Set("Content-Type", "application/json") res.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(res) enc := json.NewEncoder(res)
return enc.Encode(model) return enc.Encode(model)
} }
// test if JSON requested
func isJson(req *http.Request) bool { func isJson(req *http.Request) bool {
return req.Header.Get("Accept") == "application/json" return req.Header.Get("Accept") == "application/json"
} }
// get integer parameter
func paramInt(name string, req *http.Request) int { func paramInt(name string, req *http.Request) int {
val := req.URL.Query().Get(name) val := req.URL.Query().Get(name)
if val == "" { if val == "" {
@ -259,6 +265,8 @@ func paramInt(name string, req *http.Request) int {
} }
return valInt return valInt
} }
// get order parameter
func paramOrder(req *http.Request) string { func paramOrder(req *http.Request) string {
val := req.URL.Query().Get(PARAM_ORDER) val := req.URL.Query().Get(PARAM_ORDER)
if val == "desc" || val == "asc" { if val == "desc" || val == "asc" {
@ -267,6 +275,7 @@ func paramOrder(req *http.Request) string {
return "" return ""
} }
// get common request parameters
func params(req *http.Request) *ReqParams { func params(req *http.Request) *ReqParams {
page, perpage := paramInt(PARAM_PAGE, req), paramInt(PARAM_PERPAGE, req) page, perpage := paramInt(PARAM_PAGE, req), paramInt(PARAM_PERPAGE, req)
limit := perpage limit := perpage
@ -283,27 +292,22 @@ func params(req *http.Request) *ReqParams {
return &ReqParams{limit, offset, sort, order, terms, false} return &ReqParams{limit, offset, sort, order, terms, false}
} }
// ROUTES // // single element or list elements page
func listOrId(res http.ResponseWriter, req *http.Request, url string,
func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) { listFunc func(res http.ResponseWriter, req *http.Request) error,
count, err := app.BookCount() idFunc func(idParam string, res http.ResponseWriter, req *http.Request) error) error {
if err != nil { if !strings.HasPrefix(req.URL.Path, url) {
log.Print(err) return errors.New("Invalid URL") // FIXME 404
} }
model := NewIndexModel("", count) idParam := req.URL.Path[len(url):]
if isJson(req) { if len(idParam) == 0 {
err := writeJson(res, model) return listFunc(res, req)
if err != nil {
log.Println(err)
http.Error(res, err.Error(), 500)
}
} else {
err = app.render(res, TPL_INDEX, model)
if err != nil {
log.Println(err)
}
} }
return idFunc(idParam, res, req)
} }
// LIST ELEMENTS PAGES //
func (app *Bouquins) BooksListPage(res http.ResponseWriter, req *http.Request) error { func (app *Bouquins) BooksListPage(res http.ResponseWriter, req *http.Request) error {
if isJson(req) { if isJson(req) {
books, count, more, err := app.BooksAdv(params(req)) books, count, more, err := app.BooksAdv(params(req))
@ -314,35 +318,6 @@ func (app *Bouquins) BooksListPage(res http.ResponseWriter, req *http.Request) e
} }
return errors.New("Invalid mime") return errors.New("Invalid mime")
} }
func (app *Bouquins) BookPage(idParam string, res http.ResponseWriter, req *http.Request) error {
id, err := strconv.Atoi(idParam)
if err != nil {
return nil
}
book, err := app.BookFull(int64(id))
if err != nil {
return nil
}
return app.render(res, TPL_BOOKS, &BookModel{*NewBouquinsModel(book.Title, "book"), book})
}
func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) {
var err error
var idParam = ""
if strings.HasPrefix(req.URL.Path, URL_BOOKS) {
idParam = req.URL.Path[len(URL_BOOKS):]
} else {
err = errors.New("Invalid URL") // FIXME 404
}
if len(idParam) == 0 {
err = app.BooksListPage(res, req)
} else {
err = app.BookPage(idParam, res, req)
}
if err != nil {
log.Println(err)
http.Error(res, err.Error(), 500)
}
}
func (app *Bouquins) AuthorsListPage(res http.ResponseWriter, req *http.Request) error { func (app *Bouquins) AuthorsListPage(res http.ResponseWriter, req *http.Request) error {
if isJson(req) { if isJson(req) {
authors, count, more, err := app.AuthorsAdv(params(req)) authors, count, more, err := app.AuthorsAdv(params(req))
@ -353,6 +328,30 @@ func (app *Bouquins) AuthorsListPage(res http.ResponseWriter, req *http.Request)
} }
return errors.New("Invalid mime") return errors.New("Invalid mime")
} }
func (app *Bouquins) SeriesListPage(res http.ResponseWriter, req *http.Request) error {
if isJson(req) {
series, count, more, err := app.SeriesAdv(params(req))
if err != nil {
return err
}
return writeJson(res, NewSeriesResultsModel(series, more, count))
}
return errors.New("Invalid mime")
}
// SINGLE ELEMENT PAGES //
func (app *Bouquins) BookPage(idParam string, res http.ResponseWriter, req *http.Request) error {
id, err := strconv.Atoi(idParam)
if err != nil {
return err
}
book, err := app.BookFull(int64(id))
if err != nil {
return err
}
return app.render(res, TPL_BOOKS, &BookModel{*NewBouquinsModel(book.Title, "book"), book})
}
func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *http.Request) error { func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *http.Request) error {
id, err := strconv.Atoi(idParam) id, err := strconv.Atoi(idParam)
if err != nil { if err != nil {
@ -364,34 +363,6 @@ func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *ht
} }
return app.render(res, TPL_AUTHORS, &AuthorModel{*NewBouquinsModel(author.Name, "author"), author}) return app.render(res, TPL_AUTHORS, &AuthorModel{*NewBouquinsModel(author.Name, "author"), author})
} }
func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) {
var err error
var idParam = ""
if strings.HasPrefix(req.URL.Path, URL_AUTHORS) {
idParam = req.URL.Path[len(URL_AUTHORS):]
} else {
err = errors.New("Invalid URL") // FIXME 404
}
if len(idParam) == 0 {
err = app.AuthorsListPage(res, req)
} else {
err = app.AuthorPage(idParam, res, req)
}
if err != nil {
log.Println(err)
http.Error(res, err.Error(), 500)
}
}
func (app *Bouquins) SeriesListPage(res http.ResponseWriter, req *http.Request) error {
if isJson(req) {
series, count, more, err := app.SeriesAdv(params(req))
if err != nil {
return err
}
return writeJson(res, NewSeriesResultsModel(series, more, count))
}
return errors.New("Invalid mime")
}
func (app *Bouquins) SeriePage(idParam string, res http.ResponseWriter, req *http.Request) error { func (app *Bouquins) SeriePage(idParam string, res http.ResponseWriter, req *http.Request) error {
id, err := strconv.Atoi(idParam) id, err := strconv.Atoi(idParam)
if err != nil { if err != nil {
@ -403,34 +374,32 @@ func (app *Bouquins) SeriePage(idParam string, res http.ResponseWriter, req *htt
} }
return app.render(res, TPL_SERIES, &SeriesModel{*NewBouquinsModel(series.Name, "series"), series}) return app.render(res, TPL_SERIES, &SeriesModel{*NewBouquinsModel(series.Name, "series"), series})
} }
func (app *Bouquins) SeriesPage(res http.ResponseWriter, req *http.Request) {
var err error // ROUTES //
var idParam = ""
if strings.HasPrefix(req.URL.Path, URL_SERIES) { func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) error {
idParam = req.URL.Path[len(URL_SERIES):] return listOrId(res, req, URL_BOOKS, app.BooksListPage, app.BookPage)
} else {
err = errors.New("Invalid URL") // FIXME 404
}
if len(idParam) == 0 {
err = app.SeriesListPage(res, req)
} else {
err = app.SeriePage(idParam, res, req)
}
if err != nil {
log.Println(err)
http.Error(res, err.Error(), 500)
}
} }
func (app *Bouquins) SearchPage(res http.ResponseWriter, req *http.Request) { func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) error {
model := NewSearchModel() return listOrId(res, req, URL_AUTHORS, app.AuthorsListPage, app.AuthorPage)
err := app.render(res, TPL_SEARCH, model)
if err != nil {
log.Println(err)
}
} }
func (app *Bouquins) AboutPage(res http.ResponseWriter, req *http.Request) { func (app *Bouquins) SeriesPage(res http.ResponseWriter, req *http.Request) error {
err := app.render(res, TPL_ABOUT, NewBouquinsModel("A propos", "about")) return listOrId(res, req, URL_SERIES, app.SeriesListPage, app.SeriePage)
if err != nil { }
log.Println(err) func (app *Bouquins) SearchPage(res http.ResponseWriter, req *http.Request) error {
} return app.render(res, TPL_SEARCH, NewSearchModel())
}
func (app *Bouquins) AboutPage(res http.ResponseWriter, req *http.Request) error {
return app.render(res, TPL_ABOUT, NewBouquinsModel("A propos", "about"))
}
func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) error {
count, err := app.BookCount()
if err != nil {
return err
}
model := NewIndexModel("", count)
if isJson(req) {
return writeJson(res, model)
}
return app.render(res, TPL_INDEX, model)
} }

View File

@ -172,6 +172,33 @@ type Query struct {
Desc bool Desc bool
} }
func (app *Bouquins) searchHelper(all bool, terms []string, stub, termExpr, orderExpr string) (*sql.Rows, error) {
query := stub
queryTerms := make([]interface{}, 0, len(terms))
for i, term := range terms {
queryTerms = append(queryTerms, "%"+term+"%")
query += termExpr
if i < len(terms)-1 && all {
query += STMT_BOOL_AND
}
if i < len(terms)-1 && !all {
query += STMT_BOOL_OR
}
}
query += orderExpr
log.Println("Search:", query)
stmt, err := app.DB.Prepare(query)
if err != nil {
return nil, err
}
rows, err := stmt.Query(queryTerms...)
if err != nil {
return nil, err
}
return rows, nil
}
// PREPARED STATEMENTS // // PREPARED STATEMENTS //
func (app *Bouquins) PrepareAll() error { func (app *Bouquins) PrepareAll() error {
errcount := 0 errcount := 0
@ -217,5 +244,5 @@ func (app *Bouquins) psSort(sortNameField string, qt QueryType, sort, order stri
// prepared statement without sort // prepared statement without sort
func (app *Bouquins) ps(qt QueryType) (*sql.Stmt, error) { func (app *Bouquins) ps(qt QueryType) (*sql.Stmt, error) {
return app.psSortBooks(qt, "", "") return app.psSort("any", qt, "", "")
} }

View File

@ -2,37 +2,17 @@ package bouquins
import ( import (
"database/sql" "database/sql"
"log"
) )
// SUB QUERIES // // SUB QUERIES //
func (app *Bouquins) searchAuthors(limit int, terms []string, all bool) ([]*AuthorAdv, int, error) { func (app *Bouquins) searchAuthors(limit int, terms []string, all bool) ([]*AuthorAdv, int, error) {
rows, err := app.searchHelper(all, terms, STMT_AUTHORS_SEARCH, STMT_SEARCH_TERM_AUTHOR, STMT_SEARCH_ORDER_AUTHORS)
if err != nil {
return nil, 0, err
}
authors := make([]*AuthorAdv, 0, limit) authors := make([]*AuthorAdv, 0, limit)
count := 0 count := 0
query := STMT_AUTHORS_SEARCH
queryTerms := make([]interface{}, 0, len(terms))
for i, term := range terms {
queryTerms = append(queryTerms, "%"+term+"%")
query += STMT_SEARCH_TERM_AUTHOR
if i < len(terms)-1 && all {
query += STMT_BOOL_AND
}
if i < len(terms)-1 && !all {
query += STMT_BOOL_OR
}
}
query += STMT_SEARCH_ORDER_AUTHORS
log.Println("Search:", query)
stmt, err := app.DB.Prepare(query)
if err != nil {
return nil, 0, err
}
rows, err := stmt.Query(queryTerms...)
if err != nil {
return nil, 0, err
}
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
if len(authors) <= limit { if len(authors) <= limit {

View File

@ -2,7 +2,6 @@ package bouquins
import ( import (
"database/sql" "database/sql"
"log"
) )
// MERGE SUB QUERIES // // MERGE SUB QUERIES //
@ -16,34 +15,13 @@ func assignAuthorsTagsBooks(books []*BookAdv, authors map[int64][]*Author, tags
// SUB QUERIES // // SUB QUERIES //
func (app *Bouquins) searchBooks(limit int, terms []string, all bool) ([]*BookAdv, int, error) { func (app *Bouquins) searchBooks(limit int, terms []string, all bool) ([]*BookAdv, int, error) {
books := make([]*BookAdv, 0, limit) rows, err := app.searchHelper(all, terms, STMT_BOOKS0+STMT_WHERE, STMT_SEARCH_TERM_BOOKS, STMT_SEARCH_ORDER_BOOKS)
// FIXME factorize searchAuthors,searchSeries
count := 0
query := STMT_BOOKS0 + STMT_WHERE
queryTerms := make([]interface{}, 0, len(terms))
for i, term := range terms {
queryTerms = append(queryTerms, "%"+term+"%")
query += STMT_SEARCH_TERM_BOOKS
if i < len(terms)-1 && all {
query += STMT_BOOL_AND
}
if i < len(terms)-1 && !all {
query += STMT_BOOL_OR
}
}
query += STMT_SEARCH_ORDER_BOOKS
log.Println("Search:", query)
stmt, err := app.DB.Prepare(query)
if err != nil {
return nil, 0, err
}
rows, err := stmt.Query(queryTerms...)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
defer rows.Close() defer rows.Close()
// FIXME factorize queryBooks books := make([]*BookAdv, 0, limit)
count := 0
for rows.Next() { for rows.Next() {
if len(books) <= limit { if len(books) <= limit {
book := new(BookAdv) book := new(BookAdv)

View File

@ -1,7 +1,5 @@
package bouquins package bouquins
import "log"
// MERGE SUB QUERIES // // MERGE SUB QUERIES //
func assignAuthorsSeries(series []*SeriesAdv, authors map[int64][]*Author) { func assignAuthorsSeries(series []*SeriesAdv, authors map[int64][]*Author) {
@ -13,32 +11,13 @@ func assignAuthorsSeries(series []*SeriesAdv, authors map[int64][]*Author) {
// SUB QUERIES // // SUB QUERIES //
func (app *Bouquins) searchSeries(limit int, terms []string, all bool) ([]*SeriesAdv, int, error) { func (app *Bouquins) searchSeries(limit int, terms []string, all bool) ([]*SeriesAdv, int, error) {
series := make([]*SeriesAdv, 0, limit) rows, err := app.searchHelper(all, terms, STMT_SERIES_SEARCH, STMT_SEARCH_TERM_SERIES, STMT_SEARCH_ORDER_SERIES)
count := 0
query := STMT_SERIES_SEARCH
queryTerms := make([]interface{}, 0, len(terms))
for i, term := range terms {
queryTerms = append(queryTerms, "%"+term+"%")
query += STMT_SEARCH_TERM_SERIES
if i < len(terms)-1 && all {
query += STMT_BOOL_AND
}
if i < len(terms)-1 && !all {
query += STMT_BOOL_OR
}
}
query += STMT_SEARCH_ORDER_SERIES
log.Println("Search:", query)
stmt, err := app.DB.Prepare(query)
if err != nil {
return nil, 0, err
}
rows, err := stmt.Query(queryTerms...)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
defer rows.Close() defer rows.Close()
series := make([]*SeriesAdv, 0, limit)
count := 0
for rows.Next() { for rows.Next() {
if len(series) <= limit { if len(series) <= limit {
serie := new(SeriesAdv) serie := new(SeriesAdv)

31
main.go
View File

@ -63,10 +63,7 @@ func initApp() *BouquinsConf {
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
app := &Bouquins{ app := &Bouquins{tpl, db}
tpl,
db,
}
err = app.PrepareAll() err = app.PrepareAll()
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
@ -83,13 +80,27 @@ func assets(calibre string) {
http.Handle(URL_CALIBRE, http.StripPrefix(URL_CALIBRE, http.FileServer(http.Dir(calibre)))) http.Handle(URL_CALIBRE, http.StripPrefix(URL_CALIBRE, http.FileServer(http.Dir(calibre))))
} }
func handle(f func(res http.ResponseWriter, req *http.Request) error) func(res http.ResponseWriter, req *http.Request) {
return func(res http.ResponseWriter, req *http.Request) {
err := f(res, req)
if err != nil {
log.Println(err)
http.Error(res, err.Error(), 500)
}
}
}
func handleUrl(url string, f func(res http.ResponseWriter, req *http.Request) error) {
http.HandleFunc(url, handle(f))
}
func router(app *Bouquins) { func router(app *Bouquins) {
http.HandleFunc(URL_INDEX, app.IndexPage) handleUrl(URL_INDEX, app.IndexPage)
http.HandleFunc(URL_BOOKS, app.BooksPage) handleUrl(URL_BOOKS, app.BooksPage)
http.HandleFunc(URL_AUTHORS, app.AuthorsPage) handleUrl(URL_AUTHORS, app.AuthorsPage)
http.HandleFunc(URL_SERIES, app.SeriesPage) handleUrl(URL_SERIES, app.SeriesPage)
http.HandleFunc(URL_SEARCH, app.SearchPage) handleUrl(URL_SEARCH, app.SearchPage)
http.HandleFunc(URL_ABOUT, app.AboutPage) handleUrl(URL_ABOUT, app.AboutPage)
} }
func main() { func main() {