basic importing works
This commit is contained in:
parent
ebbdb99f37
commit
39fd64ace7
@ -5,6 +5,18 @@ import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// error message
|
||||
type EventError struct {
|
||||
tcell.EventTime
|
||||
err error
|
||||
}
|
||||
|
||||
func NewEventError(err error) *EventError {
|
||||
e := &EventError{err: err}
|
||||
e.SetEventNow()
|
||||
return e
|
||||
}
|
||||
|
||||
// save change to book
|
||||
type EventBookUpdate struct {
|
||||
tcell.EventTime
|
||||
@ -66,6 +78,18 @@ func NewEventOpenImport() *EventOpenImport {
|
||||
return e
|
||||
}
|
||||
|
||||
// attempt to import given filename.csv
|
||||
type EventAttemptImport struct {
|
||||
tcell.EventTime
|
||||
filename string
|
||||
}
|
||||
|
||||
func NewEventAttemptImport(f string) *EventAttemptImport {
|
||||
e := &EventAttemptImport{filename: f}
|
||||
e.SetEventNow()
|
||||
return e
|
||||
}
|
||||
|
||||
// close import window
|
||||
type EventCloseImport struct {
|
||||
tcell.EventTime
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"git.yetaga.in/alazyreader/library/book"
|
||||
"git.yetaga.in/alazyreader/library/config"
|
||||
"git.yetaga.in/alazyreader/library/database"
|
||||
"git.yetaga.in/alazyreader/library/importer"
|
||||
"git.yetaga.in/alazyreader/library/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
@ -119,9 +120,7 @@ func main() {
|
||||
ui.StyleActive,
|
||||
false,
|
||||
)
|
||||
book := ui.NewBookDetails(&book.Book{
|
||||
Title: "test title",
|
||||
})
|
||||
activeBookDetails := ui.NewBookDetails(&book.Book{})
|
||||
|
||||
// book display (right column)
|
||||
activeBook := ui.NewBox(
|
||||
@ -129,7 +128,7 @@ func main() {
|
||||
[]string{"˄˅ select", "⏎ edit", "(esc) close"},
|
||||
ui.Contents{{
|
||||
Offsets: ui.Offsets{Top: 1, Left: 2, Bottom: 0, Right: 0},
|
||||
Container: book,
|
||||
Container: activeBookDetails,
|
||||
}},
|
||||
ui.StyleInactive,
|
||||
false,
|
||||
@ -160,6 +159,19 @@ func main() {
|
||||
)
|
||||
popup.SetVisible(false)
|
||||
|
||||
// error pop-up
|
||||
errorMessage := ui.NewEditableTextLine("")
|
||||
errorPopup := ui.NewBox(
|
||||
"error",
|
||||
[]string{"⏎ close"},
|
||||
ui.Contents{
|
||||
{Container: errorMessage, Offsets: ui.Offsets{Top: 1, Left: 1}},
|
||||
},
|
||||
ui.StyleActive.Bold(true).Foreground(tcell.ColorRed),
|
||||
false,
|
||||
)
|
||||
errorPopup.SetVisible(false)
|
||||
|
||||
// init
|
||||
screen.Clear()
|
||||
w, h := screen.Size()
|
||||
@ -215,6 +227,8 @@ func main() {
|
||||
|
||||
} else if v.Key() == tcell.KeyLeft {
|
||||
fileSelector.MoveCursor(-1)
|
||||
} else if v.Key() == tcell.KeyEnter {
|
||||
screen.PostEvent(NewEventAttemptImport(fileSelector.Text()))
|
||||
} else if v.Rune() != 0 {
|
||||
fileSelector.InsertAtCursor(v.Rune())
|
||||
}
|
||||
@ -233,16 +247,45 @@ func main() {
|
||||
activeBook.SetStyle(ui.StyleInactive)
|
||||
menu.SetStyle(ui.StyleActive)
|
||||
case *EventLoadBook:
|
||||
book.SetBook(GetBookByID(v.ID, books))
|
||||
activeBookDetails.SetBook(GetBookByID(v.ID, books))
|
||||
case *EventOpenImport:
|
||||
state.Set("ui_state", IN_IMPORT)
|
||||
menu.SetStyle(ui.StyleInactive)
|
||||
popup.SetVisible(true)
|
||||
popup.SetSize(6, 3, 5, 80)
|
||||
case *EventAttemptImport:
|
||||
// this will block other events, but it shouldn't take too long...
|
||||
f, err := os.Open(v.filename)
|
||||
if err != nil {
|
||||
screen.PostEvent(NewEventError(err))
|
||||
continue
|
||||
}
|
||||
books, err := importer.CSVToBooks(f)
|
||||
if err != nil {
|
||||
screen.PostEvent(NewEventError(err))
|
||||
continue
|
||||
}
|
||||
for b := range books {
|
||||
err = lib.AddBook(context.Background(), &books[b])
|
||||
if err != nil {
|
||||
screen.PostEvent(NewEventError(err))
|
||||
}
|
||||
}
|
||||
screen.PostEvent(NewEventCloseImport())
|
||||
allbooks, err := lib.GetAllBooks(context.Background())
|
||||
if err != nil {
|
||||
screen.PostEvent(NewEventError(err))
|
||||
}
|
||||
state.Set("library", allbooks)
|
||||
state.Set("ui_state", IN_MENU)
|
||||
case *EventCloseImport:
|
||||
state.Set("ui_state", IN_MENU)
|
||||
screen.HideCursor()
|
||||
menu.SetStyle(ui.StyleActive)
|
||||
popup.SetVisible(false)
|
||||
case *EventError:
|
||||
errorMessage.SetText(v.err.Error())
|
||||
errorPopup.SetVisible(true)
|
||||
case *EventQuit:
|
||||
screen.Fini()
|
||||
fmt.Printf("Thank you for playing Wing Commander!\n\n")
|
||||
@ -253,8 +296,10 @@ func main() {
|
||||
default:
|
||||
}
|
||||
// repaint
|
||||
l.SetMembers(Titles(state.Get("library").([]book.Book)))
|
||||
container.Draw(screen)
|
||||
popup.Draw(screen)
|
||||
errorPopup.Draw(screen)
|
||||
screen.Show()
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ func (m *MySQL) AddBook(ctx context.Context, b *book.Book) error {
|
||||
INSERT INTO `+m.tableName+`
|
||||
(title, authors, sortauthor, isbn10, isbn13, format, genre, publisher, series, volume, year, signed, description, notes, onloan, coverurl)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, )`,
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
b.Title,
|
||||
strings.Join(b.Authors, ";"),
|
||||
b.SortAuthor,
|
||||
|
65
importer/csv.go
Normal file
65
importer/csv.go
Normal file
@ -0,0 +1,65 @@
|
||||
package importer
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"git.yetaga.in/alazyreader/library/book"
|
||||
)
|
||||
|
||||
func CSVToBooks(r io.Reader) ([]book.Book, error) {
|
||||
reader := csv.NewReader(r)
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hmap := parseHeader(header)
|
||||
books := []book.Book{}
|
||||
|
||||
for {
|
||||
row, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return books, err
|
||||
}
|
||||
b := book.Book{
|
||||
Title: row[hmap["title"]],
|
||||
Authors: parseAuthors(row[hmap["author"]]),
|
||||
SortAuthor: row[hmap["authorlast"]],
|
||||
ISBN10: row[hmap["isbn-10"]],
|
||||
ISBN13: row[hmap["isbn-13"]],
|
||||
Format: row[hmap["format"]],
|
||||
Genre: row[hmap["genre"]],
|
||||
Publisher: row[hmap["publisher"]],
|
||||
Series: row[hmap["series"]],
|
||||
Volume: row[hmap["volume"]],
|
||||
Year: row[hmap["year"]],
|
||||
Signed: row[hmap["signed"]] == "yes", // convert from known string to bool
|
||||
Description: row[hmap["description"]],
|
||||
Notes: row[hmap["notes"]],
|
||||
OnLoan: row[hmap["onloan"]],
|
||||
CoverURL: row[hmap["coverurl"]],
|
||||
}
|
||||
books = append(books, b)
|
||||
}
|
||||
return books, nil
|
||||
}
|
||||
|
||||
func parseHeader(header []string) map[string]int {
|
||||
m := make(map[string]int, len(header)-1)
|
||||
for i := range header {
|
||||
m[strings.TrimSpace(strings.ToLower(header[i]))] = i
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func parseAuthors(a string) []string {
|
||||
as := strings.Split(a, ";")
|
||||
for i := range as {
|
||||
as[i] = strings.TrimSpace(as[i])
|
||||
}
|
||||
return as
|
||||
}
|
Loading…
Reference in New Issue
Block a user