Book page
This commit is contained in:
parent
b021a2a1c7
commit
a1df078f90
@ -6,6 +6,8 @@ import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -21,6 +23,14 @@ const (
|
||||
LIST_AUTHORS = "authors"
|
||||
LIST_SERIES = "series"
|
||||
LIST_BOOKS = "books"
|
||||
|
||||
URL_INDEX = "/"
|
||||
URL_BOOKS = "/books/"
|
||||
URL_AUTHORS = "/authors/"
|
||||
URL_SERIES = "/series/"
|
||||
URL_JS = "/js/"
|
||||
URL_CSS = "/css/"
|
||||
URL_FONTS = "/fonts/"
|
||||
)
|
||||
|
||||
type Bouquins struct {
|
||||
@ -42,7 +52,7 @@ type Series struct {
|
||||
type Book struct {
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
SeriesIndex int `json:"series_idx,omitempty"`
|
||||
SeriesIndex float64 `json:"series_idx,omitempty"`
|
||||
Series *Series `json:"series,omitempty"`
|
||||
}
|
||||
|
||||
@ -87,7 +97,7 @@ type AuthorFull struct {
|
||||
|
||||
type BookFull struct {
|
||||
BookAdv
|
||||
Data []BookData `json:"data,omitempty"`
|
||||
Data []*BookData `json:"data,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Pubdate int64 `json:"pubdate,omitempty"`
|
||||
Isbn string `json:"isbn,omitempty"`
|
||||
@ -187,7 +197,16 @@ func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
}
|
||||
func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) {
|
||||
book, err := app.BookFull(123)
|
||||
if !strings.HasPrefix(req.URL.Path, URL_BOOKS) {
|
||||
// FIXME 404
|
||||
log.Fatalln("Invalid URL")
|
||||
}
|
||||
id, err := strconv.Atoi(req.URL.Path[len(URL_BOOKS):])
|
||||
if err != nil {
|
||||
// FIXME 404
|
||||
log.Fatalln(err)
|
||||
}
|
||||
book, err := app.BookFull(int64(id))
|
||||
if err != nil {
|
||||
// FIXME 500
|
||||
log.Fatalln(err)
|
||||
|
129
bouquins/db.go
129
bouquins/db.go
@ -53,9 +53,9 @@ const (
|
||||
|
||||
DEF_LIM = 10
|
||||
|
||||
BOOKS QueryType = 0
|
||||
BOOKS_TAGS QueryType = 1
|
||||
BOOKS_AUTHORS QueryType = 2
|
||||
BOOKS QueryType = iota
|
||||
BOOKS_TAGS QueryType = iota
|
||||
BOOKS_AUTHORS QueryType = iota
|
||||
)
|
||||
|
||||
var QUERIES = map[Query]string{
|
||||
@ -63,12 +63,10 @@ var QUERIES = map[Query]string{
|
||||
Query{BOOKS, true, false}: STMT_BOOKS_TITLE_ASC,
|
||||
Query{BOOKS, false, true}: STMT_BOOKS_ID_DESC,
|
||||
Query{BOOKS, false, false}: STMT_BOOKS_ID_ASC,
|
||||
|
||||
Query{BOOKS_TAGS, true, true}: STMT_BOOKS_TAGS_TITLE_DESC,
|
||||
Query{BOOKS_TAGS, true, false}: STMT_BOOKS_TAGS_TITLE_ASC,
|
||||
Query{BOOKS_TAGS, false, true}: STMT_BOOKS_TAGS_ID_DESC,
|
||||
Query{BOOKS_TAGS, false, false}: STMT_BOOKS_TAGS_ID_ASC,
|
||||
|
||||
Query{BOOKS_AUTHORS, true, true}: STMT_BOOKS_AUTHORS_TITLE_DESC,
|
||||
Query{BOOKS_AUTHORS, true, false}: STMT_BOOKS_AUTHORS_TITLE_ASC,
|
||||
Query{BOOKS_AUTHORS, false, true}: STMT_BOOKS_AUTHORS_ID_DESC,
|
||||
@ -83,7 +81,7 @@ type Query struct {
|
||||
}
|
||||
|
||||
// PREPARED STATEMENTS //
|
||||
func (app *Bouquins) ps(qt QueryType, sort, order string) (*sql.Stmt, error) {
|
||||
func (app *Bouquins) psBooks(qt QueryType, sort, order string) (*sql.Stmt, error) {
|
||||
//TODO cache
|
||||
query := QUERIES[Query{qt, sort == "title", order == "desc"}]
|
||||
log.Println(query)
|
||||
@ -102,7 +100,7 @@ func assignAuthorsTagsBooks(books []*BookAdv, authors map[int64][]*Author, tags
|
||||
|
||||
func (app *Bouquins) queryBooks(limit, offset int, sort, order string) ([]*BookAdv, error) {
|
||||
books := make([]*BookAdv, 0, limit)
|
||||
stmt, err := app.ps(BOOKS, sort, order)
|
||||
stmt, err := app.psBooks(BOOKS, sort, order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +131,7 @@ func (app *Bouquins) queryBooks(limit, offset int, sort, order string) ([]*BookA
|
||||
|
||||
func (app *Bouquins) queryBooksAuthors(limit, offset int, sort, order string) (map[int64][]*Author, error) {
|
||||
authors := make(map[int64][]*Author)
|
||||
stmt, err := app.ps(BOOKS_AUTHORS, sort, order)
|
||||
stmt, err := app.psBooks(BOOKS_AUTHORS, sort, order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -160,8 +158,7 @@ func (app *Bouquins) queryBooksAuthors(limit, offset int, sort, order string) (m
|
||||
}
|
||||
|
||||
func (app *Bouquins) queryBooksTags(limit, offset int, sort, order string) (map[int64][]string, error) {
|
||||
tags := make(map[int64][]string)
|
||||
stmt, err := app.ps(BOOKS_TAGS, sort, order)
|
||||
stmt, err := app.psBooks(BOOKS_TAGS, sort, order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -169,6 +166,7 @@ func (app *Bouquins) queryBooksTags(limit, offset int, sort, order string) (map[
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tags := make(map[int64][]string)
|
||||
for rows.Next() {
|
||||
var tag string
|
||||
var book int64
|
||||
@ -187,6 +185,96 @@ func (app *Bouquins) queryBooksTags(limit, offset int, sort, order string) (map[
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
func (app *Bouquins) queryBook(id int64) (*BookFull, error) {
|
||||
stmt, err := app.DB.Prepare(STMT_BOOK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
book := new(BookFull)
|
||||
var seriesIdx sql.NullFloat64
|
||||
var seriesId, timestamp, pubdate sql.NullInt64
|
||||
var seriesName, isbn, lccn, uuid, lang, publisher sql.NullString
|
||||
var cover sql.NullBool
|
||||
err = stmt.QueryRow(id).Scan(&book.Id, &book.Title, &seriesIdx, &seriesName, &seriesId,
|
||||
×tamp, &pubdate, &isbn, &lccn, &book.Path, &uuid, &cover, &lang, &publisher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if seriesId.Valid && seriesName.Valid && seriesIdx.Valid {
|
||||
book.SeriesIndex = seriesIdx.Float64
|
||||
book.Series = &Series{seriesId.Int64, seriesName.String}
|
||||
}
|
||||
if timestamp.Valid {
|
||||
book.Timestamp = timestamp.Int64
|
||||
}
|
||||
if pubdate.Valid {
|
||||
book.Pubdate = pubdate.Int64
|
||||
}
|
||||
if isbn.Valid {
|
||||
book.Isbn = isbn.String
|
||||
}
|
||||
if lccn.Valid {
|
||||
book.Lccn = lccn.String
|
||||
}
|
||||
if uuid.Valid {
|
||||
book.Uuid = uuid.String
|
||||
}
|
||||
if lang.Valid {
|
||||
book.Lang = lang.String
|
||||
}
|
||||
if publisher.Valid {
|
||||
book.Publisher = publisher.String
|
||||
}
|
||||
if cover.Valid {
|
||||
book.Has_cover = cover.Bool
|
||||
}
|
||||
return book, nil
|
||||
}
|
||||
func (app *Bouquins) queryBookTags(book *BookFull) error {
|
||||
stmt, err := app.DB.Prepare(STMT_BOOK_TAGS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := stmt.Query(book.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for rows.Next() {
|
||||
var tag string
|
||||
if err = rows.Scan(&tag); err != nil {
|
||||
return err
|
||||
}
|
||||
book.Tags = append(book.Tags, tag)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (app *Bouquins) queryBookAuthors(book *BookFull) error {
|
||||
stmt, err := app.DB.Prepare(STMT_BOOK_DATA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := stmt.Query(book.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for rows.Next() {
|
||||
data := new(BookData)
|
||||
if err = rows.Scan(&data.Name, &data.Format, &data.Size); err != nil {
|
||||
return err
|
||||
}
|
||||
book.Data = append(book.Data, data)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (app *Bouquins) queryBookData(book *BookFull) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DB LOADS //
|
||||
|
||||
@ -204,10 +292,23 @@ func (app *Bouquins) AuthorsAdv(limit, offset int, sort, order string) ([]*Autho
|
||||
}
|
||||
|
||||
func (app *Bouquins) BookFull(id int64) (*BookFull, error) {
|
||||
b := new(BookFull)
|
||||
b.Id = id
|
||||
b.Title = "test"
|
||||
return b, nil
|
||||
book, err := app.queryBook(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = app.queryBookTags(book)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = app.queryBookAuthors(book)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = app.queryBookData(book)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return book, nil
|
||||
}
|
||||
|
||||
func (app *Bouquins) BooksAdv(limit, offset int, sort, order string) ([]*BookAdv, error) {
|
||||
|
30
main.go
30
main.go
@ -10,7 +10,7 @@ import (
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
||||
"meutel.net/meutel/go-bouquins/bouquins"
|
||||
. "meutel.net/meutel/go-bouquins/bouquins"
|
||||
)
|
||||
|
||||
type BouquinsConf struct {
|
||||
@ -18,16 +18,6 @@ type BouquinsConf struct {
|
||||
DbPath string `json:"db-path"`
|
||||
}
|
||||
|
||||
const (
|
||||
INDEX = "/"
|
||||
BOOKS = "/books/"
|
||||
AUTHORS = "/authors/"
|
||||
SERIES = "/series/"
|
||||
JS = "/js/"
|
||||
CSS = "/css/"
|
||||
FONTS = "/fonts/"
|
||||
)
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
// load config
|
||||
@ -69,7 +59,7 @@ func initApp() *BouquinsConf {
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
app := &bouquins.Bouquins{
|
||||
app := &Bouquins{
|
||||
tpl,
|
||||
db,
|
||||
}
|
||||
@ -79,16 +69,16 @@ func initApp() *BouquinsConf {
|
||||
}
|
||||
|
||||
func assets() {
|
||||
http.Handle(JS, http.FileServer(http.Dir("assets")))
|
||||
http.Handle(CSS, http.FileServer(http.Dir("assets")))
|
||||
http.Handle(FONTS, http.FileServer(http.Dir("assets")))
|
||||
http.Handle(URL_JS, http.FileServer(http.Dir("assets")))
|
||||
http.Handle(URL_CSS, http.FileServer(http.Dir("assets")))
|
||||
http.Handle(URL_FONTS, http.FileServer(http.Dir("assets")))
|
||||
}
|
||||
|
||||
func router(app *bouquins.Bouquins) {
|
||||
http.HandleFunc(INDEX, app.IndexPage)
|
||||
http.HandleFunc(BOOKS, app.BooksPage)
|
||||
http.HandleFunc(AUTHORS, app.AuthorsPage)
|
||||
http.HandleFunc(SERIES, app.SeriesPage)
|
||||
func router(app *Bouquins) {
|
||||
http.HandleFunc(URL_INDEX, app.IndexPage)
|
||||
http.HandleFunc(URL_BOOKS, app.BooksPage)
|
||||
http.HandleFunc(URL_AUTHORS, app.AuthorsPage)
|
||||
http.HandleFunc(URL_SERIES, app.SeriesPage)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -1,9 +1,12 @@
|
||||
{{ template "header.html" . }}
|
||||
<div class="container" id="app">
|
||||
<div class="page-header" v-if="book.id">
|
||||
<div class="row" v-if="book.has_cover">
|
||||
{{ if .Id }}
|
||||
<div class="page-header">
|
||||
{{ if .Has_cover }}
|
||||
<div class="row">
|
||||
<img :src="bookCover(book)" alt="Pas de couverture" title="Couverture" class="img-responsive img-rounded"/>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-9">
|
||||
<h1>
|
||||
@ -11,46 +14,53 @@
|
||||
{{ .Title }}
|
||||
</h1>
|
||||
</div>
|
||||
{{ if gt (len .Data) 0 }}
|
||||
<div class="col-xs-12 col-md-3 text-right">
|
||||
<template v-for="data in book.data">
|
||||
{{ range .Data }}
|
||||
<a :href="bookLink(book, data)" class="btn btn-success">
|
||||
<span class="glyphicon glyphicon-download-alt"></span> Télécharger
|
||||
{{ .Data.Format }} ({{/* .FormatBytes(data.size) */}})
|
||||
{{ .Format }} ({{ .Size }})
|
||||
</a>
|
||||
</template>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert" v-else>Aucun livre sélectionné</div>
|
||||
<div class="row" v-if="book.id">
|
||||
<div class="row">
|
||||
<h2>
|
||||
<span class="glyphicon glyphicon-user"></span> Auteur{{/* .Book.Authors.Length > 1 ? 's' : '' */}}
|
||||
<span class="glyphicon glyphicon-user"></span> Auteur{{ if gt (len .Authors) 1 }}s{{ end }}
|
||||
</h2>
|
||||
<ul>
|
||||
<li v-for="author in book.authors">
|
||||
<a :href="'author.html?id='+author.id">{{ .Author.Name }}</a>
|
||||
{{ range .Authors }}
|
||||
<li>
|
||||
<a href="/authors/{{.Id}}">{{ .Name }}</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
<h2 v-if="book.series">
|
||||
{{ if .Series }}
|
||||
<h2>
|
||||
<span class="glyphicon glyphicon-list"></span> Serie
|
||||
</h2>
|
||||
<div v-if="book.series">
|
||||
<a :href="'series.html?id='+book.series.id">{{ .Series.Name }}</a>
|
||||
<span class="badge">{{ .Series.Idx }}</span>
|
||||
<div>
|
||||
<a href="/series/{{ .Series.Id }}">{{ .Series.Name }}</a>
|
||||
<span class="badge">{{ .SeriesIndex }}</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<h2><span class="glyphicon glyphicon-globe"></span> Langue</h2>
|
||||
<ul><li>{{/* .Lang.toUpperCase() */}}</li></ul>
|
||||
<ul><li>{{ .Lang }}</li></ul>
|
||||
|
||||
<h2 v-if="book.tags">
|
||||
{{ if gt (len .Tags) 0 }}
|
||||
<h2>
|
||||
<span class="glyphicon glyphicon-tags"></span> Tags
|
||||
</h2>
|
||||
<div v-if="book.tags">
|
||||
<template v-for="tag in book.tags">
|
||||
<span class="label label-info">{{ .Tag }}</span>
|
||||
</template>
|
||||
{{ range .Tags }}
|
||||
<span class="label label-info">{{ . }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<h2>Détails</h2>
|
||||
<ul>
|
||||
@ -58,5 +68,8 @@
|
||||
<li v-if="book.publisher"><strong>Editeur</strong> {{ .Publisher }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="alert alert-danger" role="alert">Aucun livre sélectionné</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ template "footer.html" . }}
|
||||
|
@ -1,3 +1,3 @@
|
||||
<script src="js/vue.min.js"></script>
|
||||
<script src="/js/vue.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user