commit 8bcb48e012e1a5f9447c6348d84cc4c4ec1ac90e Author: Ettore Dreucci Date: Thu Apr 23 16:17:29 2020 +0200 First alpha version Signed-off-by: Ettore Dreucci diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92370a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# ---> Go +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# ---> VisualStudioCode +.settings +*.code-workspace +.vscode diff --git a/fattureSanRossore.go b/fattureSanRossore.go new file mode 100644 index 0000000..61026ff --- /dev/null +++ b/fattureSanRossore.go @@ -0,0 +1,208 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/extrame/xls" + "github.com/pdfcpu/pdfcpu/pkg/api" + "github.com/sqweek/dialog" + "mvdan.cc/xurls/v2" +) + +var tmp string = os.TempDir() + +func getInvoiceIDs(fileName string) []string { + xlFile, err := xls.Open(fileName, "utf-8") + if err != nil { + log.Fatalf("Impossibile aprire il file xls: %v\n", err) + } + + sheet := xlFile.GetSheet(0) + if sheet == nil { + log.Fatalf("Impossibile aprire il foglio nell'xls: %v\n", err) + } + + var invoiceIDs []string + + for i := 4; i <= int(sheet.MaxRow); i++ { + row := sheet.Row(i) + if row.Col(8) != "" { + id := strings.ReplaceAll(row.Col(8), "/", "-") + invoiceIDs = append(invoiceIDs, id) + } + } + return invoiceIDs +} + +func convertXLStoFODS(fileName string) string { + var sofficePath string = "libreoffice" + if runtime.GOOS == "windows" { + sofficePath = filepath.FromSlash("C:/Program Files/LibreOffice/program/soffice.exe") + } + cmd := exec.Command(sofficePath, "--convert-to", "fods", "--outdir", tmp, fileName) + err := cmd.Run() + if err != nil { + log.Fatalf("Impossibile convertire l'XLS in FODS: %v\n", err) + } + return (tmp + "/" + strings.TrimSuffix(filepath.Base(fileName), filepath.Ext(fileName)) + ".fods") +} + +func getInvoiceURLs(fileName string) []string { + fods := convertXLStoFODS(fileName) + f, err := os.Open(fods) + if err != nil { + log.Fatalf("Impossibile aprire il FODS convertito: %v\n", err) + } + defer func() { + err = f.Close() + if err != nil { + log.Printf("Impossibile chiudere il file %v: %v\n", fods, err) + } + err = os.Remove(fods) + if err != nil { + log.Printf("Impossibile eliminare il file temporaneo %v: %v\n", fods, err) + } + }() + var invoiceURLs []string + s := bufio.NewScanner(f) + for s.Scan() { + line := s.Text() + if strings.Contains(line, "http://report.casadicurasanrossore.it:9146/files/get?type=invoice&id=") { + url := xurls.Strict().FindString(line) + url = strings.ReplaceAll(url, "&", "&") + invoiceURLs = append(invoiceURLs, url) + } + } + if err := s.Err(); err != nil { + log.Fatalf("Impossibile leggere dal file %v: %v\n", fods, err) + } + return invoiceURLs +} + +func checkFile(fileName string) string { + _, err := os.Stat(fileName) + if err != nil { + log.Fatalf("Errore nell'apertura del file %v: %v\n", fileName, err) + } + absPath, err := filepath.Abs(fileName) + if err != nil { + log.Fatalf("Impossibile recuperare il percorso assoluto del file %v: %v\n", fileName, err) + } + return absPath +} + +func downloadFile(fileName string, url string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + out, err := os.Create(fileName) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + return err +} + +func downloadInvoices(ids []string, urls []string) []string { + if len(ids) != len(urls) { + log.Fatalf("Il numero di fatture da scaricare non corrisponde al numero di URL individuati nel file") + } + + dir := filepath.FromSlash(tmp + "/pdfInvoices" + "_" + time.Now().Format("20060102")) + err := os.Mkdir(dir, os.ModePerm) + if err != nil { + log.Fatalf("Impossibile creare la directory temporanea di salvataggio %v: %v\n", dir, err) + } + + downloadCount := 0 + var downloadedFiles []string + for i := 0; i < len(ids); i++ { + out := filepath.FromSlash(dir + "/" + ids[i] + ".pdf") + + fmt.Printf("Scaricamento di %v\n", ids[i]) + err = downloadFile(out, urls[i]) + if err != nil { + log.Printf("Impossibile scaricare il file %v: %v\n", urls[i], err) + } else { + downloadCount++ + downloadedFiles = append(downloadedFiles, out) + } + } + fmt.Printf("Scaricate %d/%d fatture\n", downloadCount, len(ids)) + return downloadedFiles +} + +func mergeInvoices(files []string) string { + out, err := dialog.File().Filter("PDF files", "pdf").Title("Scegli dove salvare le fatture unite").Save() + if err != nil { + log.Fatalf("Impossibile recuperare il file selezionato: %v\n", err) + } + if filepath.Ext(out) == "" { + out += ".pdf" + } + err = api.MergeFile(files, out, nil) + if err != nil { + log.Fatalf("Impossibile unire i pdf: %v\nFatture singole non rimosse\n", err) + } + dir := filepath.Dir(files[0]) + for _, file := range files { + err = os.Remove(file) + if err != nil { + log.Printf("Impossibile eliminare la fattura singola %v: %v\n", file, err) + } + } + err = os.Remove(dir) + if err != nil { + log.Printf("Impossibile eliminare la directory temporanea %v: %v\n", dir, err) + } + return out +} + +func openPDF(fileName string) { + if runtime.GOOS == "windows" { + cmd := exec.Command("cmd", "/C start "+fileName) + err := cmd.Run() + if err != nil { + log.Fatalf("Impossibile aprire il pdf con le fatture unite: %v\n", err) + } + } + //TODO for Linux +} + +func main() { + var fileName string + + args := os.Args + if len(args) < 2 { + var err error + fileName, err = dialog.File().Filter("XLS files", "xls").Load() + if err != nil { + log.Fatalf("Impossibile recuperare il file selezionato: %v\n", err) + } + } else { + fileName = args[1] + } + + filePath := checkFile(fileName) + IDs := getInvoiceIDs(filePath) + URLs := getInvoiceURLs(filePath) + dlFiles := downloadInvoices(IDs, URLs) + pdf := mergeInvoices(dlFiles) + openPDF(pdf) + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e28dc9e --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/Noettore/ccsrReportDownloader + +go 1.14 + +require ( + github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 // indirect + github.com/extrame/xls v0.0.1 + github.com/gotk3/gotk3 v0.4.0 // indirect + github.com/pdfcpu/pdfcpu v0.3.2 + github.com/sqweek/dialog v0.0.0-20200304031853-0dcd55bfe06a + mvdan.cc/xurls/v2 v2.2.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5143524 --- /dev/null +++ b/go.sum @@ -0,0 +1,45 @@ +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I= +github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 h1:n+nk0bNe2+gVbRI8WRbLFVwwcBQ0rr5p+gzkKb6ol8c= +github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7/go.mod h1:GPpMrAfHdb8IdQ1/R2uIRBsNfnPnwsYE9YYI5WyY1zw= +github.com/extrame/xls v0.0.1 h1:jI7L/o3z73TyyENPopsLS/Jlekm3nF1a/kF5hKBvy/k= +github.com/extrame/xls v0.0.1/go.mod h1:iACcgahst7BboCpIMSpnFs4SKyU9ZjsvZBfNbUxZOJI= +github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/gotk3/gotk3 v0.4.0 h1:TIuhyQitGeRTxOQIV3AJlYtEWWJpC74JHwAIsxlH8MU= +github.com/gotk3/gotk3 v0.4.0/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo= +github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk= +github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ= +github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk= +github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94= +github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-gtk v0.0.0-20180216084204-5a311a1830ab/go.mod h1:PwzwfeB5syFHXORC3MtPylVcjIoTDT/9cvkKpEndGVI= +github.com/mattn/go-pointer v0.0.0-20171114154726-1d30dc4b6f28/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= +github.com/pdfcpu/pdfcpu v0.3.2 h1:oHnvW3KUed/jVLnNcN5FyJsmInXAyyfoZ4yG3mxJdk8= +github.com/pdfcpu/pdfcpu v0.3.2/go.mod h1:/ULj8B76ZnB4445B0yuSASQqlN0kEO+khtEnmPdEoXU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/skelterjohn/go.wde v0.0.0-20180104102407-a0324cbf3ffe/go.mod h1:zXxNsJHeUYIqpg890APBNEn9GoCbA4Cdnvuv3mx4fBk= +github.com/sqweek/dialog v0.0.0-20200304031853-0dcd55bfe06a h1:BHv3lo0aZg2IPfeBfgYFjq48DoKehP+JC9dtACUEmT4= +github.com/sqweek/dialog v0.0.0-20200304031853-0dcd55bfe06a/go.mod h1:QSrNdZLZB8VoFPGlZ2vDuA2oNaVdhld3g0PZLc7soX8= +github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= +github.com/tealeg/xlsx/v2 v2.0.1 h1:RP+VEscpPFjH2FnpKh1p9HVLAk1htqb9Urcxi2AU1ns= +github.com/tealeg/xlsx/v2 v2.0.1/go.mod h1:l9GvhCCjdaIGkAyZcFedDALcYcXUOei55f6umRMOz9c= +golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0= +golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc= +mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A= +mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=