Revert import book summary feature
This commit is contained in:
@@ -24,10 +24,6 @@ func TestPostImportBookHandler_Ok(t *testing.T) {
|
|||||||
assert.Equal(t, "Emily Brontë", book.Author)
|
assert.Equal(t, "Emily Brontë", book.Author)
|
||||||
assert.Equal(t, "isbn:9782253004752", book.InventaireId)
|
assert.Equal(t, "isbn:9782253004752", book.InventaireId)
|
||||||
assert.Equal(t, "/static/bookcover/44abbcbdc1092212c2bae66f5165019dac1e2a7b.webp", book.CoverPath)
|
assert.Equal(t, "/static/bookcover/44abbcbdc1092212c2bae66f5165019dac1e2a7b.webp", book.CoverPath)
|
||||||
expectedDesc := `Roman unique, à la croisée du fantastique et du romantisme, ce texte inclassable bouleverse les codes du XIXe siècle par sa violence émotionnelle, sa narration fragmentée et ses personnages à fleur de peau.
|
|
||||||
Sur les landes battues par les vents, à l'ombre des murs de Hurlevent, se joue une tragédie d'amour et de vengeance entre Catherine et Heathcliff - deux âmes tourmentées, liées par une passion aussi absolue que destructrice.
|
|
||||||
Sublimée par l'univers graphique intense d'Isabella Mazzanti, cette édition s'impose comme un objet littéraire à part, mêlant innovations narratives et force d'évocation. Les images semblent vibrer d'un souffle secret, comme si le vent y faisait surgir, en silence, le tumulte des passions.`
|
|
||||||
assert.Equal(t, expectedDesc, book.Summary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostImportBookHandler_OkAuthorKey(t *testing.T) {
|
func TestPostImportBookHandler_OkAuthorKey(t *testing.T) {
|
||||||
@@ -37,9 +33,6 @@ func TestPostImportBookHandler_OkAuthorKey(t *testing.T) {
|
|||||||
assert.Equal(t, "Philip K. Dick", book.Author)
|
assert.Equal(t, "Philip K. Dick", book.Author)
|
||||||
assert.Equal(t, "isbn:9782290033630", book.InventaireId)
|
assert.Equal(t, "isbn:9782290033630", book.InventaireId)
|
||||||
assert.Equal(t, "/static/bookcover/1d1493159d031224a42b37c4417fcbb8c76b00bd.webp", book.CoverPath)
|
assert.Equal(t, "/static/bookcover/1d1493159d031224a42b37c4417fcbb8c76b00bd.webp", book.CoverPath)
|
||||||
expectedDesc := `Les bombes étaient finalement tombées. Malgré l'équilibre de la terreur, un jour un homme avait été assez fou pour appuyer sur le bouton. Cependant, dans ce coin perdu de Californie, la vie continuait. Pour Bonny Keller, toujours perturbée malgré six ans d'analyse ; pour Bruno Bluthgeld, l'un des responsables de la grande catastrophe ; pour Hoppy, le phocomèle, l'ancien bébé thalidomide doté de pouvoirs supranormaux... Elle continuait aussi pour Walt Dangerfield, l'astronaute expédié sur mars, mais dont la cabine s'était satellisée autour de la terre. Là, à l'abri des radiations, il s'était transformé en une sorte de super disc-jockey dont l'écoute était devenue une drogue pour tous les survivants...
|
|
||||||
Philip K. Dick (1928-1982). A travers une œuvre imposante, il ne cessera de traiter ses thèmes de prédilection : la juxtaposition de deux niveaux de réalité — l'un "objectivement" déterminé, l'autre n'étant qu'un monde d'apparences — et la poranoïa qu'impliquent ces manipulations de la réalité dont personne ne tonnait jamais le degré exact de "virtualité".`
|
|
||||||
assert.Equal(t, expectedDesc, book.Summary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostImportBookHandler_NoOLID(t *testing.T) {
|
func TestPostImportBookHandler_NoOLID(t *testing.T) {
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
package babelio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.artlef.fr/bibliomane/internal/callapiutils"
|
|
||||||
"git.artlef.fr/bibliomane/internal/myvalidator"
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
|
||||||
"golang.org/x/text/encoding/charmap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type babelioSearchArg struct {
|
|
||||||
Term string `json:"term"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type babelioSearchResult struct {
|
|
||||||
//only parsing the url
|
|
||||||
Url string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDescriptionFromISBN(baseUrl string, isbn string) (string, error) {
|
|
||||||
url, err := searchPageIsbn(baseUrl, isbn)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
//we either find the full summary, or we have to make another call to get it.
|
|
||||||
fullSummary, payloadToQuery, err := parseBookPage(baseUrl, url)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if fullSummary != "" {
|
|
||||||
return decodeAndCleanText(strings.NewReader(fullSummary)), err
|
|
||||||
} else if payloadToQuery != "" {
|
|
||||||
return queryDescription(baseUrl, payloadToQuery)
|
|
||||||
} else {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func searchPageIsbn(baseUrl, isbn string) (string, error) {
|
|
||||||
searchUrl, err := callapiutils.ComputeUrl(baseUrl, "aj_recherche.php")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
term := babelioSearchArg{Term: isbn}
|
|
||||||
var searchResults []babelioSearchResult
|
|
||||||
callapiutils.FetchAndParseResultFromPost(searchUrl, &term, &searchResults)
|
|
||||||
if len(searchResults) == 0 {
|
|
||||||
return "", myvalidator.TranslatedError{Err: errors.New("ISBNNotFoundBabelio"), Arg: isbn}
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchResults[0].Url, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBookPage(baseUrl, bookUrl string) (string, string, error) {
|
|
||||||
|
|
||||||
url, err := callapiutils.ComputeUrl(baseUrl, bookUrl)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
resp, err := http.Get(url.String())
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
|
||||||
//we either find the full summary, or we have to make another call to get it.
|
|
||||||
fullsummary := ""
|
|
||||||
jsToParse := ""
|
|
||||||
doc.Find(".livre_resume").Each(func(i int, s *goquery.Selection) {
|
|
||||||
onclick, ok := s.Find("a").Attr("onclick")
|
|
||||||
if ok {
|
|
||||||
jsToParse = onclick
|
|
||||||
} else {
|
|
||||||
fullsummary = s.Text()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if fullsummary != "" {
|
|
||||||
return fullsummary, "", nil
|
|
||||||
}
|
|
||||||
typeStr, idObj, err := extractNumbersFromExpression(jsToParse)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return "", fmt.Sprintf("type=%s&id_obj=%s", typeStr, idObj), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractNumbersFromExpression(jsToParse string) (string, string, error) {
|
|
||||||
splitted := strings.Split(jsToParse, ",")
|
|
||||||
if len(splitted) < 3 {
|
|
||||||
return "", "", myvalidator.TranslatedError{Err: errors.New("BabelioParseError")}
|
|
||||||
}
|
|
||||||
if len(splitted[2]) < 3 {
|
|
||||||
return "", "", myvalidator.TranslatedError{Err: errors.New("BabelioParseError")}
|
|
||||||
}
|
|
||||||
return splitted[1], splitted[2][:len(splitted[2])-2], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryDescription(baseUrl string, payloadToQuery string) (string, error) {
|
|
||||||
url, err := callapiutils.ComputeUrl(baseUrl, "aj_voir_plus_a.php")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
resp, err := http.Post(url.String(),
|
|
||||||
"application/x-www-form-urlencoded; charset=UTF-8",
|
|
||||||
strings.NewReader(payloadToQuery))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return "", myvalidator.TranslatedError{Err: fmt.Errorf("BabelioFetchDescError")}
|
|
||||||
}
|
|
||||||
return decodeAndCleanText(resp.Body), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeAndCleanText(reader io.Reader) string {
|
|
||||||
tr := charmap.Windows1252.NewDecoder().Reader(reader)
|
|
||||||
var decodedString strings.Builder
|
|
||||||
io.Copy(&decodedString, tr)
|
|
||||||
return strings.TrimSpace(strings.ReplaceAll(decodedString.String(), "<br>", "\n"))
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package babelio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetDescriptionFromISBN_Philip(t *testing.T) {
|
|
||||||
desc, err := GetDescriptionFromISBN("https://www.babelio.com", "9782290033630")
|
|
||||||
expectedDesc := `Les bombes étaient finalement tombées. Malgré l'équilibre de la terreur, un jour un homme avait été assez fou pour appuyer sur le bouton. Cependant, dans ce coin perdu de Californie, la vie continuait. Pour Bonny Keller, toujours perturbée malgré six ans d'analyse ; pour Bruno Bluthgeld, l'un des responsables de la grande catastrophe ; pour Hoppy, le phocomèle, l'ancien bébé thalidomide doté de pouvoirs supranormaux... Elle continuait aussi pour Walt Dangerfield, l'astronaute expédié sur mars, mais dont la cabine s'était satellisée autour de la terre. Là, à l'abri des radiations, il s'était transformé en une sorte de super disc-jockey dont l'écoute était devenue une drogue pour tous les survivants...
|
|
||||||
Philip K. Dick (1928-1982). A travers une œuvre imposante, il ne cessera de traiter ses thèmes de prédilection : la juxtaposition de deux niveaux de réalité — l'un "objectivement" déterminé, l'autre n'étant qu'un monde d'apparences — et la poranoïa qu'impliquent ces manipulations de la réalité dont personne ne tonnait jamais le degré exact de "virtualité".`
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.Equal(t, expectedDesc, desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetDescriptionFromISBN_Emily(t *testing.T) {
|
|
||||||
desc, err := GetDescriptionFromISBN("https://www.babelio.com", "9782253004752")
|
|
||||||
expectedDesc := `Roman unique, à la croisée du fantastique et du romantisme, ce texte inclassable bouleverse les codes du XIXe siècle par sa violence émotionnelle, sa narration fragmentée et ses personnages à fleur de peau.
|
|
||||||
Sur les landes battues par les vents, à l'ombre des murs de Hurlevent, se joue une tragédie d'amour et de vengeance entre Catherine et Heathcliff - deux âmes tourmentées, liées par une passion aussi absolue que destructrice.
|
|
||||||
Sublimée par l'univers graphique intense d'Isabella Mazzanti, cette édition s'impose comme un objet littéraire à part, mêlant innovations narratives et force d'évocation. Les images semblent vibrer d'un souffle secret, comme si le vent y faisait surgir, en silence, le tumulte des passions.`
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
assert.Equal(t, expectedDesc, desc)
|
|
||||||
}
|
|
||||||
@@ -22,19 +22,17 @@ type CLI struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port string `toml:"port" short:"p" default:"8080" help:"Port to listen on for the server." comment:"Port to listen on for the server."`
|
Port string `toml:"port" short:"p" default:"8080" help:"Port to listen on for the server." comment:"Port to listen on for the server."`
|
||||||
DatabaseFilePath string `toml:"database-file-path" short:"d" default:"bibliomane.db" type:"path" help:"Path to sqlite database file." comment:"Path to sqlite database file."`
|
DatabaseFilePath string `toml:"database-file-path" short:"d" default:"bibliomane.db" type:"path" help:"Path to sqlite database file." comment:"Path to sqlite database file."`
|
||||||
DemoDataPath string `toml:"demo-data-path" help:"Path to the sql file to load for demo data." comment:"Path to the sql file to load for demo data."`
|
DemoDataPath string `toml:"demo-data-path" help:"Path to the sql file to load for demo data." comment:"Path to the sql file to load for demo data."`
|
||||||
JWTKey string `toml:"jwt-key" help:"Key used to encrypt JWT." comment:"Key used to encrypt the generated JWT. Encoded in base64. If empty a random one will be generated on every restart."`
|
JWTKey string `toml:"jwt-key" help:"Key used to encrypt JWT." comment:"Key used to encrypt the generated JWT. Encoded in base64. If empty a random one will be generated on every restart."`
|
||||||
ImageFolderPath string `toml:"image-folder-path" short:"i" default:"img" type:"path" help:"Folder where uploaded files will be stored." comment:"Folder where uploaded files will be stored."`
|
ImageFolderPath string `toml:"image-folder-path" short:"i" default:"img" type:"path" help:"Folder where uploaded files will be stored." comment:"Folder where uploaded files will be stored."`
|
||||||
Limit int `toml:"limit" default:"100" help:"A single API call will return at most this number of records." comment:"A single API call will return at most this number of records."`
|
Limit int `toml:"limit" default:"100" help:"A single API call will return at most this number of records." comment:"A single API call will return at most this number of records."`
|
||||||
InventaireUrl string `toml:"inventaire-url" default:"https://inventaire.io" help:"An inventaire.io instance URL." comment:"An inventaire.io instance URL."`
|
InventaireUrl string `toml:"inventaire-url" default:"https://inventaire.io" help:"An inventaire.io instance URL." comment:"An inventaire.io instance URL."`
|
||||||
BookDescriptionFromBabelio bool `toml:"book-description-from-babelio" default:"false" help:"Activate fetching description from babelio.com." comment:"Activate fetching description from babelio.com."`
|
DisableRegistration bool `toml:"disable-registration" short:"n" default:"false" help:"Disable new account creation." comment:"Disable new account creation."`
|
||||||
BabelioUrl string `toml:"babelio-url" default:"https://www.babelio.com" comment:"Link to babelio website."`
|
DemoMode bool `toml:"demo-mode" short:"D" default:"false" help:"Activate demo mode." comment:"Activate demo mode: anyone connecting to the instance will be logged in as a single user."`
|
||||||
DisableRegistration bool `toml:"disable-registration" short:"n" default:"false" help:"Disable new account creation." comment:"Disable new account creation."`
|
DemoUsername string `toml:"demo-username" default:"demo" help:"Name of the single user used for the demo." comment:"Name of the single user used for the demo."`
|
||||||
DemoMode bool `toml:"demo-mode" short:"D" default:"false" help:"Activate demo mode." comment:"Activate demo mode: anyone connecting to the instance will be logged in as a single user."`
|
AddUser UserListAsStrings `toml:"add-user" short:"a" help:"Add users on startup following htpasswd bcrypt format." comment:"Add users on startup following htpasswd bcrypt format, example: [\"demo:$2y$10$UHR2646SZo2W.Rhna7bn5eWNLXWJZ/Sa3oLd9RlxlXs57Bwp6isOS\",\"user:$2y$10$3WYUp.VDpzJRywtrxO1s/uWfUIKpTE4yh5B1d2RCef3hvczYbEWTC\"]"`
|
||||||
DemoUsername string `toml:"demo-username" default:"demo" help:"Name of the single user used for the demo." comment:"Name of the single user used for the demo."`
|
|
||||||
AddUser UserListAsStrings `toml:"add-user" short:"a" help:"Add users on startup following htpasswd bcrypt format." comment:"Add users on startup following htpasswd bcrypt format, example: [\"demo:$2y$10$UHR2646SZo2W.Rhna7bn5eWNLXWJZ/Sa3oLd9RlxlXs57Bwp6isOS\",\"user:$2y$10$3WYUp.VDpzJRywtrxO1s/uWfUIKpTE4yh5B1d2RCef3hvczYbEWTC\"]"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserListAsStrings []string
|
type UserListAsStrings []string
|
||||||
@@ -50,19 +48,17 @@ func (u UserListAsStrings) Validate() error {
|
|||||||
|
|
||||||
func defaultConfig() CLI {
|
func defaultConfig() CLI {
|
||||||
c := Config{
|
c := Config{
|
||||||
Port: "8080",
|
Port: "8080",
|
||||||
DatabaseFilePath: "bibliomane.db",
|
DatabaseFilePath: "bibliomane.db",
|
||||||
DemoDataPath: "",
|
DemoDataPath: "",
|
||||||
JWTKey: "",
|
JWTKey: "",
|
||||||
ImageFolderPath: "img",
|
ImageFolderPath: "img",
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
InventaireUrl: "https://inventaire.io",
|
InventaireUrl: "https://inventaire.io",
|
||||||
BookDescriptionFromBabelio: false,
|
DisableRegistration: false,
|
||||||
BabelioUrl: "https://www.babelio.com",
|
DemoMode: false,
|
||||||
DisableRegistration: false,
|
DemoUsername: "demo",
|
||||||
DemoMode: false,
|
AddUser: []string{},
|
||||||
DemoUsername: "demo",
|
|
||||||
AddUser: []string{},
|
|
||||||
}
|
}
|
||||||
return CLI{NoConfigFile: false, ConfigFilePath: "bibliomane.toml", DisableStoreJWTKeyInConfig: false, ConfigFile: c}
|
return CLI{NoConfigFile: false, ConfigFilePath: "bibliomane.toml", DisableStoreJWTKeyInConfig: false, ConfigFile: c}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.artlef.fr/bibliomane/internal/appcontext"
|
"git.artlef.fr/bibliomane/internal/appcontext"
|
||||||
"git.artlef.fr/bibliomane/internal/babelio"
|
|
||||||
"git.artlef.fr/bibliomane/internal/dto"
|
"git.artlef.fr/bibliomane/internal/dto"
|
||||||
"git.artlef.fr/bibliomane/internal/fileutils"
|
"git.artlef.fr/bibliomane/internal/fileutils"
|
||||||
"git.artlef.fr/bibliomane/internal/inventaire"
|
"git.artlef.fr/bibliomane/internal/inventaire"
|
||||||
@@ -66,25 +64,6 @@ func saveInventaireBookToDb(ac appcontext.AppContext, inventaireEdition inventai
|
|||||||
book.Cover = cover
|
book.Cover = cover
|
||||||
}
|
}
|
||||||
|
|
||||||
if ac.Config.BookDescriptionFromBabelio {
|
|
||||||
isbn := findIsbn(&inventaireEdition)
|
|
||||||
if isbn != "" {
|
|
||||||
desc, err := babelio.GetDescriptionFromISBN(ac.Config.BabelioUrl, isbn)
|
|
||||||
if err != nil {
|
|
||||||
te, isTrError := errors.AsType[myvalidator.TranslatedError](err)
|
|
||||||
var errToPrint string
|
|
||||||
if isTrError {
|
|
||||||
errToPrint = te.ToTranslatedMessage(&ac)
|
|
||||||
} else {
|
|
||||||
errToPrint = err.Error()
|
|
||||||
}
|
|
||||||
log.Println(errToPrint)
|
|
||||||
} else {
|
|
||||||
book.Summary = desc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := ac.Db.Save(&book).Error
|
err := ac.Db.Save(&book).Error
|
||||||
return &book, err
|
return &book, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user