145 lines
3.7 KiB
Go
145 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// AlbumCsvReader can read Album definition from a csv file
|
|
type AlbumCsvReader struct {
|
|
r *csv.Reader
|
|
columns map[string]int
|
|
}
|
|
|
|
const (
|
|
cID string = "IdAlbum"
|
|
cISBN = "ISBN"
|
|
cSeries = "Serie"
|
|
cNum = "Num"
|
|
cNumA = "NumA"
|
|
cTitle = "Titre"
|
|
cEditor = "Editeur"
|
|
cCollection = "Collection"
|
|
cLegalDeposit = "DL"
|
|
cPrintDate = "AI"
|
|
cState = "Etat"
|
|
cPurchaseDate = "DateAchat"
|
|
cPerso1 = "Perso1"
|
|
cPerso2 = "Perso2"
|
|
)
|
|
|
|
var requiredFields = []string{cID, cISBN, cSeries, cNum, cNumA, cTitle, cEditor, cCollection, cLegalDeposit, cPrintDate, cState, cPurchaseDate, cPerso1, cPerso2}
|
|
|
|
// NewAlbumCsvReader creates a new AlbumCsvReader from a reader
|
|
func NewAlbumCsvReader(r io.Reader) (*AlbumCsvReader, error) {
|
|
res := &AlbumCsvReader{
|
|
r: csv.NewReader(r),
|
|
columns: make(map[string]int),
|
|
}
|
|
res.r.Comma = ';'
|
|
//reads the first line
|
|
colNames, err := res.r.Read()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("album_csv: could not read first line: %s", err)
|
|
}
|
|
|
|
for i, n := range colNames {
|
|
res.columns[n] = i
|
|
}
|
|
|
|
missing := []string{}
|
|
for _, f := range requiredFields {
|
|
if _, ok := res.columns[f]; ok == false {
|
|
missing = append(missing, f)
|
|
}
|
|
}
|
|
|
|
if len(missing) != 0 {
|
|
return nil, fmt.Errorf("Invalid CSV file, missing required fields: %v", missing)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
var nullTimeRx = regexp.MustCompile(`(00/)+0000`)
|
|
|
|
func safeParseTime(format, value string, result *time.Time) error {
|
|
if len(value) == 0 || nullTimeRx.MatchString(value) == true {
|
|
*result = time.Time{}
|
|
return nil
|
|
}
|
|
var err error
|
|
*result, err = time.ParseInLocation(format, value, time.UTC)
|
|
if err != nil {
|
|
return fmt.Errorf("AlbumCsvReader: %s: %s", cLegalDeposit, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Read get the next line in the CSV and return an Album or an error,
|
|
// or io.EOF if stream is closed
|
|
func (r *AlbumCsvReader) Read() (*Album, error) {
|
|
data, err := r.r.Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := &Album{
|
|
ISBN: data[r.columns[cISBN]],
|
|
Series: data[r.columns[cSeries]],
|
|
NumA: data[r.columns[cNumA]],
|
|
Title: data[r.columns[cTitle]],
|
|
Editor: data[r.columns[cEditor]],
|
|
Collection: data[r.columns[cCollection]],
|
|
}
|
|
|
|
// simply skip new columns Name
|
|
if data[0] == "IdRevue" || data[0] == "IdParaBD" {
|
|
return r.Read()
|
|
}
|
|
|
|
ID, err := strconv.ParseInt(data[r.columns[cID]], 0, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("AlbumCsvReader: %s: %s", cID, err)
|
|
}
|
|
res.ID = AlbumID(ID)
|
|
|
|
if len(data[r.columns[cNum]]) == 0 {
|
|
res.Num = -1
|
|
} else {
|
|
n, err := strconv.ParseInt(data[r.columns[cNum]], 0, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("AlbumCsvReader: %s: %s", cNum, err)
|
|
}
|
|
res.Num = int(n)
|
|
}
|
|
|
|
err = safeParseTime("01/2006", data[r.columns[cLegalDeposit]], &(res.LegalDeposit))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("AlbumCsvReader: %s: %s", cLegalDeposit, err)
|
|
}
|
|
err = safeParseTime("01/2006", data[r.columns[cPrintDate]], &(res.PrintDate))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("AlbumCsvReader: %s: %s", cPrintDate, err)
|
|
}
|
|
|
|
state, err := strconv.ParseInt(data[r.columns[cState]], 0, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("AlbumCsvReader: %s: %s", cState, err)
|
|
}
|
|
res.State = AlbumState(state)
|
|
|
|
err = safeParseTime("02/01/2006", data[r.columns[cPurchaseDate]], &(res.PurchaseDate))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("AlbumCsvReader: %s: %s", cPurchaseDate, err)
|
|
}
|
|
|
|
res.SatID = fmt.Sprintf("%s-%s", data[r.columns[cPerso1]], data[r.columns[cPerso2]])
|
|
|
|
return res, nil
|
|
}
|