Book page

This commit is contained in:
Meutel 2017-08-01 20:00:20 +02:00
parent b021a2a1c7
commit a1df078f90
5 changed files with 197 additions and 74 deletions

View File

@ -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,16 +97,16 @@ type AuthorFull struct {
type BookFull struct {
BookAdv
Data []BookData `json:"data,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Pubdate int64 `json:"pubdate,omitempty"`
Isbn string `json:"isbn,omitempty"`
Lccn string `json:"lccn,omitempty"`
Path string `json:"path,omitempty"`
Uuid string `json:"uuid,omitempty"`
Has_cover bool `json:"has_cover,omitempty"`
Lang string `json:"lang,omitempty"`
Publisher string `json:"publisher,omitempty"`
Data []*BookData `json:"data,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Pubdate int64 `json:"pubdate,omitempty"`
Isbn string `json:"isbn,omitempty"`
Lccn string `json:"lccn,omitempty"`
Path string `json:"path,omitempty"`
Uuid string `json:"uuid,omitempty"`
Has_cover bool `json:"has_cover,omitempty"`
Lang string `json:"lang,omitempty"`
Publisher string `json:"publisher,omitempty"`
}
type SeriesAdv struct {
@ -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)

View File

@ -53,22 +53,20 @@ 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{
Query{BOOKS, true, true}: STMT_BOOKS_TITLE_DESC,
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, true, true}: STMT_BOOKS_TITLE_DESC,
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,
&timestamp, &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
View File

@ -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() {

View File

@ -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 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>&nbsp;
</template>
{{ range .Tags }}
<span class="label label-info">{{ . }}</span>&nbsp;
{{ 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" . }}

View File

@ -1,3 +1,3 @@
<script src="js/vue.min.js"></script>
<script src="/js/vue.min.js"></script>
</body>
</html>