Csv conversion with go - csv

I am still a noob at golang, but I will try to explain what I want to create.
I have this:
I want to convert this same table into a csv file like
This is the code I have:
func maakCSVBestand() {
rapportage := haalRapportage()
opdrachten := rapportage.Opdrachten
csvFile, err := os.Create("Rapportage Urenverantwoording.csv")
if err != nil {
fmt.Println(err)
}
defer csvFile.Close()
writer := csv.NewWriter(csvFile)
writer.Comma = ';'
writer.Write([]string{"Datum", "Naam", "Aantal uren gewerkt", "Aantal deuren gemaakt", "Informatie"})
for i := 0; i < len(opdrachten); i++ {
var row []string
persoon := opdrachten[i].Persoon
row = append(row, opdrachten[i].Datum.String())
for j := 0; j < len(persoon); j++ {
row = append(row, persoon[j].Naam+" "+persoon[j].Achternaam)
row = append(row, strconv.FormatFloat(persoon[j].UrenGewerkt, 'g', 2, 64))
row = append(row, strconv.Itoa(persoon[j].AantalDeurenGemaakt))
row = append(row, persoon[j].Informatie)
}
writer.Write(row)
}
writer.Flush()
}
But this creates
What am i doing wrong?

Write one row for each iteration of the inner loop. Clear the first column after the first iteration:
for i := 0; i < len(opdrachten); i++ {
var row []string
persoon := opdrachten[i].Persoon
row = append(row, opdrachten[i].Datum.String())
for j := 0; j < len(persoon); j++ {
row = append(row, persoon[j].Naam+" "+persoon[j].Achternaam)
row = append(row, strconv.FormatFloat(persoon[j].UrenGewerkt, 'g', 2, 64))
row = append(row, strconv.Itoa(persoon[j].AantalDeurenGemaakt))
row = append(row, persoon[j].Informatie)
writer.Write(row)
row[0] = "" // clear Datum field
row = row[1:] // collect new values after blank datum
}
}

Related

Gorm mariadb cache query select in transaction

I use gorm and mariadb
there is a function to update to the terminal table and insert to the terminalLog table.
by using the transaction begin commit . before inserting I checked the terminalLog table by calling the GetByUniqueTerminalLog function.
If the GetByUniqueTerminalLog function is placed before the transaction begin, it returns the query result data.
but if the function is between the transaction begin and commit it does not return the data that was previously inserted into the terminalLog table.
what caused it?
what should i do if GetByUniqueTerminalLog is placed between transactions begin commit.
this is the source code snippet
func (r *TerminalRepository) GetByUniqueTerminalLog(ctx *gin.Context, id int64, terminal_id int64, version string, device_id string, payment_id int64) (res *model.TerminalLog, err error) {
log.Debug("TerminalRepository - GetByUniqueTerminalLog() - starting...")
db := r.DbContext.DB
if id > 0 {
db = db.Where("id = ?", id)
}
if terminal_id > 0 {
db = db.Where("terminal_id = ? ", terminal_id)
}
if payment_id > 0 {
db = db.Where("payment_id = ? ", payment_id)
}
if device_id != "" {
db = db.Where("device_id = ? ", device_id)
}
if version != "" {
db = db.Where("version = ? ", version)
}
result := db.Debug().Find(&res)
if result.Error != nil {
return res, result.Error
}
log.Debug("TerminalRepository - GetByUniqueTerminalLog() - finished.")
return res, nil
}
func (r *TerminalRepository) UpdateTerminalVersion(ctx *gin.Context, data *model.Terminal) (err error) {
log.Debug("TerminalRepository - UpdateTerminalVersion() - starting...")
if err = data.Validate(); err != nil {
return err
}
dataUpdate, err := r.GetByUnique(ctx, data.ID, "", 0, "", "")
if err != nil {
return err
}
if dataUpdate.ID < 1 {
return custom.ErrorNotFoundDB(data.TableName(), "id", data.ID)
}
TerminalLog ,err := r.GetByUniqueTerminalLog(ctx,0,dataUpdate.ID,data.Version,"",0)
dataUpdate.Version = data.Version
dataUpdate.InitAudit(constant.OPERATION_SQL_UPDATE, data.UpdatedUser)
tx := r.DbContext.DB.Begin()
result := tx.Updates(&dataUpdate)
if result.Error != nil {
tx.Rollback()
return result.Error
}
if result.RowsAffected < 1 {
tx.Rollback()
return custom.ErrorOperationDB(dataUpdate.TableName(), "update")
}
//TerminalLog ,err := r.GetByUniqueTerminalLog(ctx,0,dataUpdate.ID,data.Version,"",0)
fmt.Printf("TerminalLog %+v\n ",TerminalLog)
if TerminalLog.ID < 1 {
DataTerminalLog := model.TerminalLog{
Version: data.Version,
Activity: "UPDATE_VERSION",
TerminalId: &dataUpdate.ID,
MerchantId: &dataUpdate.MerchantId,
PaymentId: dataUpdate.PaymentId,
DeviceId: dataUpdate.DeviceId,
}
DataTerminalLog.InitAudit(constant.OPERATION_SQL_INSERT, 1)
resultInsertLog := tx.Create(&DataTerminalLog)
if resultInsertLog.Error != nil {
tx.Rollback()
return resultInsertLog.Error
}
if resultInsertLog.RowsAffected < 1 {
tx.Rollback()
return custom.ErrorOperationDB(dataUpdate.TableName(), "insert")
}
}
tx.Commit()
log.Debug("TerminalRepository - UpdateTerminalVersion() - finished.")
return nil
}

How to use results from a sql query as a variable

I'm trying to use a result from a query as an integer, so i can use it in some different calculations.
I'm fairly new at Go and programming in general(really new, just started school a few weeks back). For an assignment for school I need to calculate the 'doorlooptijd'(nr of months a customer has to pay) based on the customers age.
When i run below code i keep getting the error: 'cannot use leeftijdAlsText (type *sql.Rows) as type string in argument to strconv.Atoi'
leeftijd := "SELECT TIMESTAMPDIFF(YEAR, k.geboortedatum, NOW()) AS leeftijd FROM klant k WHERE k.klantnummer = ?"
leeftijdAlsText, err := db.Query(leeftijd, nummerKlant)
if err != nil {
fmt.Println("Error found.")
panic(err)
}
var huidigeLeeftijd int
if leeftijdAlsText.Next() {
err := leeftijdAlsText.Scan(&leeftijdAlsText)
if err != nil {
fmt.Println("Error found")
panic(err)
}
}
huidigeLeeftijd, _ = strconv.Atoi(leeftijdAlsText)
var doorlooptijd int
if huidigeLeeftijd < 45 {
doorlooptijd = 120
} else if huidigeLeeftijd > 45 && huidigeLeeftijd < 55 {
doorlooptijd = 90
} else if huidigeLeeftijd > 55 {
doorlooptijd = 60
}
When this works, i need to insert the doorlooptijd in a new row in my database, together with some other information about the customer.

Combining data from multiple cells into one JSON object

I am trying to combine data from multiple cells from an excel spreadsheet into one JSON encoded string. I cannot figure out how to do so, the code below is creating a new JSON object per cell. How do I differentiate the cells to combine into the same JSON string?
package main
import (
"fmt"
"github.com/tealeg/xlsx"
"encoding/json"
)
func main() {
excelFileName := "/Users/isaacmelton/Desktop/Test_Data.xlsx"
xlFile, err := xlsx.OpenFile(excelFileName)
if err != nil {
fmt.Printf("Cannot parse data")
}
for _, sheet := range xlFile.Sheets {
for _, row := range sheet.Rows {
fmt.Printf("\n")
for x, cell := range row.Cells {
if x == 3 || x == 5 {
data := map[string]string{"d_name": cell.String(), "name": cell.String()}
json_data, _ := json.Marshal(data)
fmt.Println(string(json_data))
}
}
}
}
}
Running the above code results in the following:
{"foo":"cell1","bar":"cell1"}
{"foo":"cell2","bar":"cell2"}
I expect something like this:
{"foo":"cell1", "bar":"cell2"}
If I right understand your request you just need to define root element, add cells into it and marshal this element rather than individual cells.
root := []map[string]string{}
for x, cell := range row.Cells {
if x == 3 || x == 5 {
root = append(root, map[string]string{"d_name": cell.String(), "name": cell.String()})
}
}
json_data, _ := json.Marshal(root)
fmt.Println(string(json_data))
http://play.golang.org/p/SHnShHvW_0
You may use
a, err := row.Cells[3].String()
b, err := row.Cells[5].String()
Like this working code:
package main
import (
"encoding/json"
"fmt"
"github.com/tealeg/xlsx"
)
func main() {
xlFile, err := xlsx.OpenFile(`Test_Data.xlsx`)
if err != nil {
panic(err)
}
for _, sheet := range xlFile.Sheets {
for _, row := range sheet.Rows {
//for x, cell := range row.Cells {
//if x == 3 || x == 5 {
a, err := row.Cells[3].String()
if err != nil {
panic(err)
}
b, err := row.Cells[5].String()
if err != nil {
panic(err)
}
data := map[string]string{"d_name": a, "name": b}
json_data, err := json.Marshal(data)
if err != nil {
panic(err)
}
fmt.Println(string(json_data))
//}
//}
}
}
}
output:
{"d_name":"1000","name":"a"}
{"d_name":"2000","name":"b"}
{"d_name":"3000","name":"c"}
{"d_name":"4000","name":"d"}
{"d_name":"5000","name":"e"}
input file content:
1 10 100 1000 10000 a
2 20 200 2000 20000 b
3 30 300 3000 30000 c
4 40 400 4000 40000 d
5 50 500 5000 50000 e

Worker pool to handle queries

I'm pretty new to Go and looking for a way to handle 3000 queries using 100 workers and ensuring a connection for every worker (MySQL is already configured with more than 100 connections). This is my attempt:
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
var query *sql.Stmt
func worker(jobs <-chan int, results chan<- int) {
for _ = range jobs {
_, e := query.Exec("a")
if e != nil {
panic(e.Error())
}
results <- 1
}
}
func main() {
workers := 100
db, e := sql.Open("mysql", "foo:foo#/foo")
if e != nil {
panic(e.Error())
}
db.SetMaxOpenConns(workers)
db.SetMaxIdleConns(workers)
defer db.Close()
query, e = db.Prepare("INSERT INTO foo (foo) values(?)")
if e != nil {
panic(e.Error())
}
total := 30000
jobs := make(chan int, total)
results := make(chan int, total)
for w := 0; w < workers; w++ {
go worker(jobs, results)
}
for j := 0; j < total; j++ {
jobs <- j
}
close(jobs)
for r := 0; r < total; r++ {
<-results
}
}
It's working, but I'm not sure if is the best way of doing it.
Please, if you think this is opinion based or is not a good question at all, just mark it to be closed and leave a comment explaining why.
What you've got fundamentally works, but to get rid of buffering, you need to be writing to jobs and reading from results at the same time. Otherwise, your process ends up stuck--workers can't send results because nothing is receiving them, and you can't insert jobs because workers are blocked.
Here's a boiled-down example on the Playground of how to do a work queue that pushes jobs in the background as it receives results in main:
package main
import "fmt"
func worker(jobs <-chan int, results chan<- int) {
for _ = range jobs {
// ...do work here...
results <- 1
}
}
func main() {
workers := 10
total := 30
jobs := make(chan int)
results := make(chan int)
// start workers
for w := 0; w < workers; w++ {
go worker(jobs, results)
}
// insert jobs in background
go func() {
for j := 0; j < total; j++ {
jobs <- j
}
}()
// collect results
for i := 0; i < total; i++ {
<-results
fmt.Printf(".")
}
close(jobs)
}
For that particular code to work, you have to know how many results you'll get. If you don't know that (say, each job could produce zero or multiple results), you can use a sync.WaitGroup to wait for the workers to finish, then close the result stream:
package main
import (
"fmt"
"sync"
)
func worker(jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
for _ = range jobs {
// ...do work here...
results <- 1
}
wg.Done()
}
func main() {
workers := 10
total := 30
jobs := make(chan int)
results := make(chan int)
wg := &sync.WaitGroup{}
// start workers
for w := 0; w < workers; w++ {
wg.Add(1)
go worker(jobs, results, wg)
}
// insert jobs in background
go func() {
for j := 0; j < total; j++ {
jobs <- j
}
close(jobs)
wg.Wait()
// all workers are done so no more results
close(results)
}()
// collect results
for _ = range results {
fmt.Printf(".")
}
}
There are many other more complicated tricks one can do to stop all workers after an error happens, put results into the same order as the original jobs, or do other things like that. Sounds as if the basic version works here, though.

Golang Net.IP to IPv6 (from MySQL) as Decimal(39,0) Conversion?

I have a database that stores IPv4 and IPv6 addresses as decimal(39,0). I need to convert a Golang Net.IP to this format. I have done it for IPv4 as follows:
func ipv4ToInt(IPv4Addr net.IP) int64 {
bits := strings.Split(IPv4Addr.String(), ".")
b0, _ := strconv.Atoi(bits[0])
b1, _ := strconv.Atoi(bits[1])
b2, _ := strconv.Atoi(bits[2])
b3, _ := strconv.Atoi(bits[3])
var sum int64
sum += int64(b0) << 24
sum += int64(b1) << 16
sum += int64(b2) << 8
sum += int64(b3)
return sum
}
I am trying the same with IPv6:
func ipv6ToInt(IPv6Addr net.IP) Int128 {
bits := strings.Split(IPv6Addr.String(), ":")
var arr [4]int64
var arr1 [4]uint64
for i := 0; i < 4; i++ {
arr[i], _ = strconv.ParseInt(bits[i], 16, 64)
}
for i := 0; i < 4; i++ {
arr1[i], _ = strconv.ParseUint(bits[i], 16, 64)
}
int1 := arr[0]
for i := 0; i < 4; i++ {
int1 = (int1 << 16) + arr[i]
}
int2 := arr1[0]
for i := 0; i < 4; i++ {
int2 = (int2 << 16) + arr1[i]
}
var IPv6Int Int128
IPv6Int.H = int1
IPv6Int.L = int2
return IPv6Int
}
Where int128 is
type Int128 struct {
H int64
L uint64
}
The result should look like:
42540578165168461141553663388954918914
from the IPv6 addr:
2001:470:0:76::2
Thanks!
EDIT, ANSWER:
Thanks to the people in #go-nuts, the answer is as follows:
func ipv6ToInt(IPv6Addr net.IP) *big.Int {
IPv6Int := big.NewInt(0)
IPv6Int.SetBytes(IPv6Addr)
return IPv6Int
}
The same works for IPv6, just do IP.To4() first.
Thanks to the people in #go-nuts, the answer is as follows:
func ipv6ToInt(IPv6Addr net.IP) *big.Int {
IPv6Int := big.NewInt(0)
IPv6Int.SetBytes(IPv6Addr)
return IPv6Int
}
The same works for IPv4, just do IP.To4() first.